VC控件学习笔记

来源:互联网 发布:衣柜设计 知乎 编辑:程序博客网 时间:2024/04/30 14:02
 
VC控件学习笔记  
摘自http://blog.163.com/rb_shen/blog/static/624226472008022113631938/

CEdit重新审视

一个从没想过的问题今天终于出现了!我自己都感到以外!呵呵,什么问题呢?可能你也没想过哦。即使想过,你解答了没有?

是一个聊天的程序,类似于QQ的界面。简化一点说把。(注意是简化)一个基于对话框的的程序,只有2个编辑框m_edit1,m_edit2, 还有一个是按钮。现在我要做的就是在 m_edit2中输入,然后点击按钮,内容就跑到了m_edit1中。并且每次点击后,上次 m_edit1的内容还会留下来,当然是聊天嘛,每次发送的内容要在m_edit1的最后显示。呵呵,听起来很简单哦

注意我最后一句话。如果你采用连加的话,像这样:

m_edit1.GetWindowText(m_str1);

m_edit2.GetWindowText(m_str2);

m_str1+=m_str2;

m_edit1.SetWindowText(m_str1);

内容是出现了,但是光标还在第一个位置,这样如果m_edit1是一个多行的程序,有垂直滚动条,那么就麻烦了,前面我已经说了,要求是  “每次发送的内容要在m_edit1的最后显示”,也就是要求滚动条要移动到最后,按照上面那种方法,滚动条都在上面。

怎么办哪?没有关于滚动条的函数啊。其实不是滚动条的原因,原因是光标。光标还在第一个位置啊。明白了,我们要把光标移动到最后。

于是上msdn了。找“CEdit”,“class members“,这么多函数,还不知哪个是移动光标的函数呢。一个一个的试。posfromchar,SetCaretPos ,......都不行啊,晕了。。。

正绝望呢,突然想起了久违的setsel和GetSel  我们都知道CEdit是用来选中编辑框中的内容的,它们会不会有用呢?管他呢,先 全部选中再说。

m_edit1.SetFocus();

m_edit1.SetSel(0,-1);

啊!选中了!不,我不是惊喜这个(不然我会被看贴的砍死的)。除了选中之余,光标也移动到了最后!呵呵,成功了!

再想想,还不行,差一点。这样岂不是我每次要全部选中?不好。既然选中是移动光标的话,那我干脆一不做二不休,函数照用,但是我不选,呵呵,这样写:

m_edit.SetFocus();

m_edit1.SetSel(m_str1.GetLength(),m_str1.GetLength());

搞定了!

高兴之余,我又想了一下这个问题。SetSel可以确定光标位置,那么GetSel不就可以知道光标的位置吗?试试:(另外一个程序)

DWORD   curPos  =   m_edit.GetSel();  

 CString   sCurStr ;

 m_edit.GetWindowText(sCurStr);  

 sCurStr.Insert(LOWORD(curPos),   str);  

 m_edit.SetWindowText(sCurStr);

这样的话,随便定义光标在一个位置,都可以插入东西了,呵呵。

感慨就是函数的功能还真多!看来要慢慢理解!

 

 控件的实时拖动

由于一个小项目的需要,必须做控件的拖曳。以前还真没有想过这种问题。先来看看有没有处理消息,比如drag什么的,很遗憾没有。再来看看有没有函数,好像也没有,郁闷。没办法了。只好用绝招,自己做一个派生类把,下面我就以编辑框为例,简单说明一下拖曳的过程。

1.从CEdit派生一个类为CmyEdit

2. 添加左键处理函数:

void Cmyedit::OnLButtonDown(UINT nFlags, CPoint point)
{
          // TODO: Add your message handler code here and/or call default
  
        pp=point;      // pp是Cmyedit的成员变量
          SetTimer(5,10,NULL);  // 引发定时器
          CEdit::OnLButtonDown(nFlags, point);
}

 

void Cmyedit::OnLButtonUp(UINT nFlags, CPoint point)
{
         // TODO: Add your message handler code here and/or call default
        KillTimer(5);  // 关闭定时器
        CEdit::OnLButtonUp(nFlags, point);
}

3. 添加定时消息处理:

void Cmyedit::OnTimer(UINT nIDEvent)
{
 // TODO: Add your message handler code here and/or call default
 
if(nIDEvent==5){ 

//获得鼠标当前位置,转换为父窗口内的坐标

 CPoint cursor_Pos;
 GetCursorPos(&cursor_Pos);
 GetParent()->ScreenToClient(&cursor_Pos);

//鼠标原来单击位置pp,转换为所在父窗口内的坐标

 CPoint edit_Pos=pp;
 ClientToScreen(&edit_Pos);
 GetParent()->ScreenToClient(&edit_Pos);

// 获得控件的大小,并且转换到父窗口的坐标,主要是为了获得左上角的坐标,方便下面的movewindow调用

 CRect edit_rect;
 GetWindowRect(edit_rect);
 GetParent()->ScreenToClient(&edit_rect);

// 利用向量相等的性质求得鼠标移动后的新左上角坐标

 int x=edit_rect.left+(cursor_Pos.x-edit_Pos.x);
 int y=edit_rect.top+(cursor_Pos.y-edit_Pos.y);
 MoveWindow(x,y,edit_rect.Width(),edit_rect.Height());

 }
CEdit::OnTimer(nIDEvent);

}

 

4.改变鼠标形状

BOOL Cmyedit::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
        // TODO: Add your message handler code here and/or call default

        SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEALL));
        return TRUE;

}

这样就结束了。可以到处用了。符合拖动的要求了。

 抽屉菜单的简单实现

看到 QQ的抽屉,也想自己做一个,于是做了个最简单的,界面如图所示

 


  

这是一个基于对话框的程序,有3个按钮,有4个对话框,1个是主窗口,还有3个是子窗口,因此需要3个对话框的资源风格为: “下层,对话框架“

下面是具体设计过程:

 1.给3个子窗口(对话框)分别派生一个对话框类,名为Cmydlg1,Cmydlg2,Cmydlg3;

 2. Cmydlg (主窗口)中添加成员:

     Cmydlg1 *dlg1;

      Cmydlg2 *dlg2;

      Cmydlg3 *dlg3;

      int m_left;    // 按钮的左边坐标

      int m_right;  // 按钮的右边坐标

      int m_buttonHeight;// 按钮的高度

      int m_buttonWidth; // 按钮的宽度

      int m_dlgHeight;  // 子对话框的高度,而宽度与按钮应该相同

      CButton m_button[3]; // 3个按钮,下面会动态创建

 3.初始化

 BOOL CMyDlg::OnInitDialog()

{

      CDialog::OnInitDialog();

……….

      dlg1=new Cmydlg1;

      dlg2=new Cmydlg2;

      dlg3=new Cmydlg3;

      // 创建3个按钮,注意它的位置   

      for(int i=0;i<3;i++)

      {

             m_button[i].Create("hehe",WS_CHILD|WS_VISIBLE, \

                    CRect(m_left,m_buttonHeight*i,m_right,m_buttonHeight* (i+1)),this,IDC_BUTTON+i);

      }

      // 创建非模态窗口(即充当子窗口)

      dlg1->Create(IDD_DIALOG1,this);

      dlg2->Create(IDD_DIALOG2,this);

      dlg3->Create(IDD_DIALOG3,this);

      // 首先移动3个子窗口的位置,以后都不用再移动了,只需要显示或者不显示

      dlg1->MoveWindow(m_left,m_buttonHeight,m_buttonWidth,m_dlgHeight);

      dlg2->MoveWindow(m_left,m_buttonHeight*2,m_buttonWidth,m_dlgHeight);

      dlg3->MoveWindow(m_left,m_buttonHeight*3,m_buttonWidth,m_dlgHeight);

      // 成员函数

      show(0);

      return TRUE;  // return TRUE  unless you set the focus to a control

}

 4.显示函数

void CMyDlg::show(int index)

{

      dlg1->ShowWindow(! (index-0));       // 判断是显示哪个窗体

      dlg2->ShowWindow(! (index-1));

      dlg3->ShowWindow(! (index-2));

// 显示窗体以后就要移动相应按钮的位置,处理一下几何位置关系,不细说了

      for(int i=0;i<=index;i++)

      {

m_button[i].MoveWindow( m_left, m_buttonHeight*i,m_buttonWidth,

m_buttonHeight );

      }

      for(i=index+1;i<3;i++)

      {

             m_button[i].MoveWindow( m_left,m_buttonHeight*i+m_dlgHeight,m_buttonWidth,m_buttonHeight );

      }

}

 5.添加按钮的消息映射

.h :

      // Generated message map functions

      //{{AFX_MSG(CMyDlg)

      virtual BOOL OnInitDialog();

      afx_msg void OnSysCommand(UINT nID, LPARAM lParam);

      afx_msg void OnPaint();

      afx_msg HCURSOR OnQueryDragIcon();

      

      //}}AFX_MSG

      afx_msg void OnClick(UINT nID);

 

 .cpp:

BEGIN_MESSAGE_MAP(CMyDlg, CDialog)

      //{{AFX_MSG_MAP(CMyDlg)

      ON_WM_SYSCOMMAND()

      ON_WM_PAINT()

      ON_WM_QUERYDRAGICON()

      

      //}}AFX_MSG_MAP

      ON_COMMAND_RANGE(IDC_BUTTON+0,IDC_BUTTON+2,OnClick)

END_MESSAGE_MAP()

 6.消息响应

void CMyDlg::OnClick(UINT nID)

{

      int index=nID-IDC_BUTTON;

      show(index);

}

 7.画边框

void CMyDlg::OnPaint()

{

。。。。。。。。。          

             CPaintDC dc(this);

             CBrush brush(RGB(255,0,0));

             dc.FrameRect(CRect(m_left-1,-1,m_right+1,m_buttonHeight*3+m_dlgHeight+1),&brush);

             CDialog::OnPaint();

      }

}

 完成!值得干一杯了!当然你还可以优化一下界面!

 轻松画背景

 在用MFC编程的时候,尤其是设置界面的时候,大家都会觉得VC的控件属性设置还比不上VB.连个颜色都设置不了.但也正是这样,MFC提供给我们的自由也就越多,我们可以随意派生自己的控件类,想怎么做就怎么做.VB不行.

 

但是派生一个自己的控件类,然后去自绘,也不是容易的事情.在不断的探索中,我发现了一个简单的设置背景的方法,就是直接利用PICTURE控件.把它设置成容纳位图,然后就可以充当背景了.

 

比如有一个static控件.现在再绘制一个,PICTURE控件,放在static上面.这个时候,图像是会遮盖住static控件的.我们可以重新设置一下Tab,PICTURE控件排在前面即可.下面看看我的作品把:

 

 

CListCtrl学习笔记(1)---基础篇

 背景:CListCtrl在小型数据库中应用的还是比较多的.因此掌握它是一个很重要的技能.最近在做老师布置的一个项目,用到它,开始的时候不知所措.后来上网找了一些资料,再加上自己的探索,终于对它有一个比较全面的认识.下面我简单讲讲它的一些用法,以此和大家共享一下.

1.    基本风格设置

       (1)函数 ModifyStyle( )

     (2)重要参数:   LVS_ICON // 大图标

                                 LVS_SMALLICON // 小图标

                                 LVS_LIST  // 列表

                                 LVS_REPORT // 报表

  (3)说明:

用的比较多的是最后的报表视图.因为它可以有多列,正好代表数据库中的多个属性.所以下面的用法都是针对这种风格的.当然这些风格也可以在控件的属性中设置

(4) 注意

            我们知道在窗口各种各样的风格之间,有时是可以用” |”,表示属性叠加.:

                         WS_CHILD | WS_VISIBLE;

但是上面的四种风格是不可能放在一起的.所以不要用到“ |”操作符.

也正是因此,防止用户出错,微软干脆把LVS_ICON, LVS_SMALLICON, LVS_LIST, LVS_REPORT这些都不设置成位标志,因此不能叠加.同时还设置了一个多余的掩码:

LVS_TYPEMASK.它是用来屏蔽的.

 

(5)举例:

如果我们要判断一个CListCtrl的风格:

             DWORD dwStyle = m_listctrl.GetStyle( );

//判断是否大图标样式

             If ( dwStyle & LVS_ICON )

                           …….

      这种写法是错误的.正确的写法涉及到掩码:

                    DWORD dwStyle = m_listctrl.GetStyle( ) & LVS_TYPEMASK;

                    If ( dwStyle = = LVS_ICON)

                    …….

      同理,我们在改变风格时,应该这样写:

                    ModifyStyle( LVS_TYPEMASK, LVS_ICON);

 2.    扩展风格设置

    (1) 函数: SetExtendedStyle( )  GetExtendedStyle ( )

   (2)重要参数:

             LVS_EX_FULLROWSELECT //选中某行使整行高亮(只适用与报表风格)

             LVS_EX_GRIDLINES //网格线(只适用与报表风格)

             LVS_EX_CHECKBOXES //设置checkbox状态

   (3)举例

                    DWORD dwStyle = GetExtendedStyle();

                    dwStyle |= LVS_EX_FULLROWSELECT;

                    dwStyle |= LVS_EX_GRIDLINES;

                    SetExtendedStyle(dwStyle);

  

3.其他风格设置:

 函数:SetTextColor ( )  // 设置文字颜色

  SetBkColor ( )    //设置边框颜色

  SetTextBkColor ( ) //设置文字背景颜色

//下面直接举例说明

 4.图标设置

可以给大图标风格和小图标风格设置图标:

HICON icon=AfxGetApp()->LoadIcon(IDI_ICON1);

 m_icon.Add(icon);

 m_listctrl.SetImageList(&m_icon,LVSIL_SMALL);//小图标

   m_listctrl.SetImageList(&m_icon,LVSIL_NORMAL)// 大图标

 

5插入一列

m_listctrl.InsertColumn(0,”哈哈”,LVCFMT_LEFT,80);

其中: 0是索引项,”哈哈是列标题,LVCFMT_LEFT是显示方式(靠左),80表示列的宽

 

 

6插入一行

   m_listctrl.InsertItem(0, “123”,0);//插入为第一行第一列的内容,最后一个0是图标的索引

   m_listctrl.SetItemText( 0,1, “123”) ; //设置第一行第2列的内容

      m_ listctrl.SetItemText(0,2, “123”) ; //设置第一行第3列的内容

 

 

7得到所有的行数

m_listctrl.GetItemCount( ) ;

 

8.得到所有的列数

m_listctrl.GetHeaderCtrl().GetItemCount( ) ;

 

 

9.得到被单击的项的行列号

void Cmylist::OnClick(NMHDR* pNMHDR, LRESULT* pResult) //单击消息

{

       NM_LISTVIEW *info=(NM_LISTVIEW*)pNMHDR;

      selectedIndex=info->iItem; // 行号

      selectedsub=info->iSubItem; // 列号

 }    

(CmylistCListCtrl的派生类,selectedIndexselectedsub为接口,方便使用)        

 

            

10.得到被单击的列头索引号

void Cmylist::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult)

{

      NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;

       selectedField=pNMListView->iSubItem;//列头号

}

同上,也是以selectedField为接口,方便用户调用

  

11.选中或者取消一行

plistctrl->SetItemState(index,LVIS_SELECTED,LVIS_SELECTED);//选中一行

plistctrl->SetItemState(index,0,LVIS_SELECTED);//取消一行

DWORD style=plistctrl->GetItemState(index,LVIS_SELECTED);//获得选中信息

 

12.获得被选中的多行

         POSITION p=m_listctrl.GetFirstSelectedItemPosition();//得到第一次选中的位置

         while(p)

             {

                    int index=m_listctrl.GetNextSelectedItem(p);

                    

             }

 

13.删除一行或者一列

      m_listctrl.DeleteItem( index );

      m_listctrl.DeleteColumn(index);

 

14.删除多行或者多列

      注意要从后面开始删起,比如,如果我想删除第一行和第二行,应该:

             m_listctrl.DeleteItem(1);

             m_listctrl.DeleteItem(0);

      而不能:

             m_listctrl.DeleteItem(0);

       m_listctrl.DeleteItem(1);

因为每次删除一行或者一列,后面的索引号都要变化,所以从后面开始删除就没关系.

如果用for循环,应该递减:

      for(int k=m_listctrl.GetItemCount( );k>=-1;k++)

             m_listctrl.DeleteItem(k);

这就是删除所有行,当然也可以用CListCtrl::DeleteAllItems

  

15根据索引号得到某一列的信息

                   HDITEM   hdi;  

                    TCHAR     lpBuffer[256];    

                    hdi.mask   =   HDI_TEXT;  

                    hdi.pszText   =   lpBuffer;  

                    hdi.cchTextMax   =   256;  

                    plistctrl->GetHeaderCtrl()->GetItem(index,   &hdi);

                    CString str=hdi.pszText;

 

CListCtrl学习笔记(2)---中级篇(1)

专题1:如何使CListCtrl完全可编辑?

 

1.      背景 :我们知道如果CListCtrl是报表样式,那么CListCtrl所提供的编辑功能只局限于第一列.也就是说只有第一列可编辑.这样显然无法满足一般数据库的要求.我们想要每个子项都能编辑.

  

2.      思路 : CEdit是一个很好的可控制编辑控件.如何把CEdit和我们的CListCtrl联系起来?一种很好的想法是------一般我们如果想编辑某一项,那么就应该去双击.双击以后就让CEdit在那里显示,当然要把大小调整和子项表格一样.如果CEdit失去了焦点,表示修改完毕,那么立即更改子项的数据,同时让CEdit隐藏.因为每次只能编辑一项,所以只需要一个CEdit就够了.

 

 3.      方法:

(1)   首先从CListCtrl派生一个类,其他已经有的变量或者函数设置我已经介绍,如果不清楚的读者,可以去参考基础篇”.

 

(2)   有一点可以肯定,我们必须响应双击事件:

void Cmylist::OnLButtonDblClk(UINT nFlags, CPoint point)

{

int index;//行号

int colnum;//列号

GetWindowRect(r);//稍后说明

GetParent()->ScreenToClient(r);//稍后说明

 

if((index=HitTestEx(point,&colnum))!=-1)

   EditSubItem(index,colnum);

 

CListCtrl::OnLButtonDblClk(nFlags, point);

}

其中HitTestEx是用来求出双击点所在的行列号,如果行号不为-1,那么就调用函数EditSubItem.这个函数会根据行列号求出该子项具体坐标,方便CEdit调整位置.

  

(3)   如何求出行列号?行号是很好求出来的,但是列号就不是很简单了,必须详细判断.

int Cmylist::HitTestEx(CPoint &point, int *pcolumn)

{

      int columnNum=0;//获取页面内首行索引号,不一定是0,要考虑滚动条的情况

   int row=GetTopIndex();// GetCountPerPage()获取在页面内行的总数

      int bottom=row+this->GetCountPerPage();//防止超出范围

      if(bottom>this->GetItemCount())

             bottom=GetItemCount();  //获取列的总数

     int ncolumncount=this->GetHeaderCtrl()->GetItemCount();//可以肯定双击点肯定在页面内,因此从页面首行索引号开始判断

      for(;row<=bottom;++row)

      {

             CRect rect;//求出行的rect

             GetItemRect(row,&rect,LVIR_BOUNDS);//点是否在行的矩形内

             if(rect.PtInRect(point))//如果点在行的矩形内,求出点在哪一列

                for(columnNum=0;columnNum<ncolumncount;columnNum++)

                 {        

                       int colwidth=this->GetColumnWidth(columnNum);//求出列的宽度

                       if(point.x>=rect.left&&point.x<=(rect.left+colwidth))

                           {

                                  *pcolumn=columnNum;

                                  return row;

                           }

                           rect.left+=colwidth;

                    }

      }

      return -1;

}

当然上面那种方法有点复杂,是完全从头开始判断.其实我们可以先利用CListCtrl提供的函数求出行号,再求列号,这样稍微简单点

int Cmylist::HitTestEx(CPoint &point, int *pcolumn)

{

      int columnNum=0;

      int row=HitTest(point);//求出行号

   int ncolumncount=this->GetHeaderCtrl()->GetItemCount();

           (2007.1.3更新)       

        LVHITTESTINFO Info;
       Info.pt=point;
       this->SubItemHitTest(&Info);
      *pcolumn=Info.iSubItem;

      if(*pcolumn>=0&&*pcolumn<ncolumncount)
      return row;

      else

       return -1;

      /*int ncolumncount=this->GetHeaderCtrl()->GetItemCount();

              Rect rect;

             GetItemRect(row,&rect,LVIR_BOUNDS);

             if(rect.PtInRect(point))

                    for(columnNum=0;columnNum<ncolumncount;columnNum++)

                    {

                           int colwidth=this->GetColumnWidth(columnNum);

                           if(point.x>=rect.left&&point.x<=(rect.left+colwidth))

                           {

                                  *pcolumn=columnNum;

                                  return row;

                           }

                           rect.left+=colwidth;

                    }*/

      

      }

 

(4)   求出具体CEdit移动坐标

int Cmylist::Item_X(int row, int column,CRect& rect_X)

{

      int offset=0;

      for(int i=0;i<column;i++)

          offset+=GetColumnWidth(i);

      CRect rect;

      GetItemRect(row,rect,LVIR_BOUNDS);

      //注意水平滚动条的影响,如果已经移动了水平滚动条,可能left0,或者超出总大小

      if(offset+rect.left<0||offset+rect.left>client_rect.right)

      {

             CSize size;//offset肯定为正,如果出现了rect.left为负

             if(offset+rect.left>0)

                    size.cx=- (offset+rect.left);

             else

                    size.cx=offset+rect.left;

             size.cy=0;//垂直不用管

             //如果某一列的一半在滚动条左边,一半在右边,就再次调整滚动条的位置.

             Scroll(size);

             rect.left - =size.cx;

      }

      rect.left+=offset+2;

      rect.right=rect.left+GetColumnWidth(column)-2;//bottomtop不用管

      rect_X=rect;

      return rect.right;

}

(5)   移动CEdit

void Cmylist::EditSubItem(int Item, int Column)

{

      CRect rect;//求出行列所在rect

      this->Item_X(Item,Column,rect);

      EditCellShow(rect,Item,Column,r);

}

 

 

 

void Cmylist::EditCellShow(CRect rect, int Item, int Column,CRect r)

 

{

      //还记得r?在开始的双击函数OnLButtonDblClk,它是CListCtrl在父窗口中的位置

   rect.left+=r.left;

    rect.top+=r.top+2;

    rect.right+=r.left;

    rect.bottom+=r.top+2;

//peditCEdit对象的指针,提供接口,只要在程序中让pedit指向一个对象即可

      pedit->MoveWindow(rect,TRUE);

      pedit->ShowWindow(TRUE); 

      pedit->SetFocus();

}

^_^!这样就完成了.效果还可以.当然你还要去响应CEdit失去焦点和得到焦点的事件.这个就不是我的任务了,因为每个人的要求不一样啊!

看看我的效果!

 

                      

 对话框条的制作CDialogBar

简单来说,就是对话框条的制作

1.创建对话框资源:在对话框资源编辑器内生成一个Dialog资源,并将其风格(Style)属性必须设置为Child,不能设置为OverlappedPopup,否则运行肯定出错;至于边界属性则随用户自己喜欢,一般都是选择None。其余属性也随用户选择,一般没有特殊要求还是选择默认的好。

2.自己派生一个继承于CDialogBar的类,注意此时由于ClassWizard没有把CDialogBar列出来,所以只好自己手动编写.h和.cpp,然后加上必要的处理函数,如:

class CmyDlgWnd : public CDialogBar 
{
public:
 CmyDlgWnd(CWnd *pParent=NULL);
 
 virtual ~CmyDlgWnd();
  //{{AFX_VIRTUAL(CmyDlgWnd)
 protected:
 virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
 //}}AFX_VIRTUAL
// Implementation
protected:
 
 // Generated message map functions
 //{{AFX_MSG(myDlgWnd)
  // NOTE: the ClassWizard will add member functions here
 //}}AFX_MSG

 DECLARE_MESSAGE_MAP()

};

3.假设在对话框添加了一个按钮,现在要响应,可以手动编写处理函数:

.h:

afx_msg void OnClose();

.cpp:

BEGIN_MESSAGE_MAP(CmyDlgWnd, CDialogBar)
 //{{AFX_MSG_MAP(CmyDlgWnd)
  // NOTE: the ClassWizard will add message map macros here
 //}}AFX_MSG_MAP

 ON_BN_CLICKED(IDC_CLOSE,OnClose)
END_MESSAGE_MAP()

4.同上,如果想添加关联变量,可以这样做:

.h:

CString  m_edit;

.cpp:

void CmyDlgWnd::DoDataExchange(CDataExchange* pDX)
{
 CDialogBar::DoDataExchange(pDX);
 //{{AFX_DATA_MAP(Csql)
 DDX_Text(pDX, IDC_EDIT1, m_edit);
 //}}AFX_DATA_MAP
}

5.处理完毕后,在CMainFrame中添加:


int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
 ...

//创建控制条    
 if(!m_wndDlg.Create(this,IDD_DIALOG2,CBRS_LEFT,100))
  return -1;
 //停泊控制条

 m_wndDlg.EnableDocking(CBRS_ORIENT_HORZ);
 m_wndToolBar.EnableDocking(CBRS_ALIGN_ANY);
 EnableDocking(CBRS_ALIGN_ANY);
 DockControlBar(&m_wndToolBar);
 DockControlBar(&m_wndDlg);
 }

6.注意此时对话框控制条并不能响应消息,因为消息被CMainFrame截获,因此要想办法把消息转发给CDialogBar,方法就是重载OnCmdMsg,具体的消息转发知识理论将在下次专题中推出:

BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
  if(CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  return TRUE;
  return m_wndDlg.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}

 

以上就是一种处理方法,还有一种方法是:(转载),该方法可以

提供ClassWizard帮助,比较好

用对话框创建CDialogBar派生的类并在CReBar上添加

 摘要:本文详细解说了CDialogBar的具体使用过程,可以做为VC++MFC新手学习总结用。

 一、创建DialogBar的派生类

首先,创建对话框资源:在对话框资源编辑器内生成一个Dialog资源,并将其风格(Style)属性必须设置为Child,不能设置为OverlappedPopup,否则运行肯定出错;至于边界属性则随用户自己喜欢,一般都是选择None。其余属性也随用户选择,一般没有特殊要求还是选择默认的好。

其次,创建基于CDialog的派生类:打开ClassWizard,为以上创建的资源添加一个以CDialog为基类的派生类(因为ClassWizard没有将CDialogBar列在基类目录清单中,所以用户只能先以CDialog类派生)。

再次,修改派生类以CDialogBar为基类:通常需要手工修改几处代码,在本例中派生类以

CDataStatus命名。(注:以后讲解中凡是手工改动都是以灰背景显示)

1 在头文件中修改继承关系

class CDataStatus : public CDialog 改为class CDataStatus : public CDialogBar

  2 在代码文件中修该构造函数继承关系

CDataStatus::CDataStatus(CWnd* pParent /*=NULL*/)

: CDialog(CDataStatus::IDD, pParent)

{

      //{{AFX_DATA_INIT(CDataStatus)

             // NOTE: the ClassWizard will add member initialization here

      //}}AFX_DATA_INIT

}

改为

CDataStatus::CDataStatus(CWnd* pParent /*=NULL*/)

{

      //{{AFX_DATA_INIT(CDataStatus)

             // NOTE: the ClassWizard will add member initialization here

      //}}AFX_DATA_INIT

}

        3 DDX绑定函数中的继承关系去掉

即将void CDataStatus::DoDataExchange(CDataExchange* pDX)

{

      CDialog::DoDataExchange(pDX);

      //{{AFX_DATA_MAP(CCurrentCheckDlg)

      ………..

      //}}AFX_DATA_MAP

}

改为

void CDataStatus::DoDataExchange(CDataExchange* pDX)

{

      //{{AFX_DATA_MAP(CCurrentCheckDlg)

      ………….

      //}}AFX_DATA_MAP

}

      4 重新初始化函数(这个相当重要,如果不这么做的话,DDX函数形同虚设,当然用户的工具条如果没有用到DDX的话当然可以不加这段代码):

首先在ClassWizardMessageMap中对消息该CDataStatus类的WM_INITDIALOG消息添加处理函数默认名为OnInitDialog

其次手工修改代码如下:

1            添加消息映射函数。由于对话框形式的初始化函数消息并未加载到消息映射内,为此我们需要手工添加,要不然代码无法拦截该工具条的初始化消息,形式如下:

BEGIN_MESSAGE_MAP(CDataStatus, CDialogBar)

      //{{AFX_MSG_MAP(CDataStatus)

      .......

      //}}AFX_MSG_MAP

END_MESSAGE_MAP()

改为:

BEGIN_MESSAGE_MAP(CDataStatus, CDialogBar)

      //{{AFX_MSG_MAP(CDataStatus)

      .......

      ON_MESSAGE(WM_INITDIALOG,OnInitDialog)

      //}}AFX_MSG_MAP

END_MESSAGE_MAP()

2            修改OnInitDialog函数,此函数并未传递参数,但是在这里我们需要让它传递参数,代码如下修改(当然头文件中,对声明也要做修改,在这里就不作赘述了)

BOOL CDataStatus::OnInitDialog()

{

      CDialogBar::OnInitDialog();

   

      // TODO: Add extra initialization here

      return TRUE;  // return TRUE unless you set the focus to a control

             // EXCEPTION: OCX Property Pages should return FALSE

}

改为:

BOOL CDataStatus::OnInitDialog(UINT wParam,LONG lParam)

{

      //CDialogBar::OnInitDialog();

       

      // TODO: Add extra initialization here

      BOOL bRet = HandleInitDialog(wParam,lParam);

      if (!UpdateData(FALSE))

      {

             TRACE("InitCDataStatus Failed");

      }

      return TRUE;  // return TRUE unless you set the focus to a control

                    // EXCEPTION: OCX Property Pages should return FALSE

}

 二、在框架类中实现该派生类的对象化

首先,在框架类的头文件内声明实例对象,本例实例化:CDataStatus   m_wndDataStatus;当然头文件中不可避免要包含新派生类的头文件。

其次,在框架类的OnCreate函数内创建对象并将对象绑定对话框资源。形式与创建ToolBar原理一样,本例实例如下:

if (!m_wndDataStatus.Create(this,IDD_DATASTATUS,WS_VISIBLE|WS_CHILD

|CBRS_SIZE_DYNAMIC|CBRS_BOTTOM,IDD_DATASTATUS))

     {

            TRACE0("Failed to create CDataStatus bar!");

            return -1;

     }

再次,最为关键的一点就是重写框架类的OnCmdMsg虚函数。如果不重写该函数,那么不光DDX功能无法实现,连最基本的OnCommand事件都无法实现。而且还得手工添加代码,形式如下:

BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra,

AFX_CMDHANDLERINFO* pHandlerInfo)

{

     // TODO: Add your specialized code here and/or call the base class

     return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);

}

改为:

BOOL CMainFrame::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)

{

     // TODO: Add your specialized code here and/or call the base class

     if (m_wndDataStatus.OnCmdMsg(nID,nCode,pExtra,pHandlerInfo))

            return    TRUE;

     return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);

}

     三、CReBar上添加该实例化对象

其实这一步倒是相当简单,只是自己以前没用过这个类,所以在这里也顺便用了一下。

首先,在框架类的头文件中用CRebar声明一个对象,如CReBar  m_wndReBar;

其次,在框架类的代码文件中的OnCreat函数体内,生成对象,代码如下:

if (!m_wndReBar.Create(this,RBS_BANDBORDERS,WS_CHILD |

WS_VISIBLE| CBRS_BOTTOM|WS_CLIPSIBLINGS|WS_CLIPCHILDREN))

     {

            TRACE0("Failed to create  Rebar \n");

         

   return -1;

     }

再次,就是将所要添加的toolbar以及新生成的CDataStatus对象m_wndDataStatus加进Rebar的对象m_wndReBar中,代码如下:

m_wndReBar.AddBar(&m_wndDataStatus,NULL,NULL,

RBBS_GRIPPERALWAYS|RBBS_FIXEDBMP);

CListCtrl学习笔记(3)---中级篇(2)

 

 

专题2:CListCtrl中如何实现排序功能?

1.排序功能的实现需要用到下面这个函数:

BOOL SortItems( PFNLVCOMPARE pfnCompare, DWORDdwData );

其中 第一个参数是回调函数的指针,后面是所要传递的参数

而回调函数一般定义如下:

int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2,
   LPARAM lParamSort);

其中lParam1是其中list的一项数据,而lparam2是另外一项,第三个参数是刚才传递过来的参数.

2.举例如下:

(1)回调函数的编写

注意回调函数必须是全局或者静态的,并且不能调用成员变量,所以要凭借最后一个参数传递某些数据.

int CALLBACK CompareFunc(LPARAM lParam1,LPARAM lParam2,LPARAM lParamSort)
{

//传递list指针
 Cmylist *list=(Cmylist*)lParamSort;

//获得指定行的索引号
 LVFINDINFO findInfo;
 findInfo.flags = LVFI_PARAM;
 findInfo.lParam = lParam1;
 int iItem1 = list->FindItem(&findInfo, -1);
 findInfo.lParam = lParam2;
 int iItem2 = list->FindItem(&findInfo, -1);

//根据指定列的索引号,得到所在项的数据
 CString strItem1 =list->GetItemText(iItem1,list->selectedField);
 CString strItem2 =list->GetItemText(iItem2,list->selectedField);

//判断是升序还是降序
  if(!list->shunxu)
  if(index==1)//判断数据类型是整型还是字符串
  return strcmp(strItem2, strItem1);
  else
  return atoi(strItem2)-atoi(strItem1);
 
 else
  if(index==1)
  return -strcmp(strItem2, strItem1);
  else
  return atoi(strItem1)-atoi(strItem2);
}

(2)调用函数的编写

如果是点击列头响应排序功能,那么请响应LVN_COLUMNCLICK:

如果是其他,例如响应菜单项:

void Cmylist::OnSheng()
{
 // TODO: Add your command handler code here
 shunxu=TRUE;
 SortItems(CompareFunc,(LPARAM)this);
}

void Cmylist::OnJiang()
{
 // TODO: Add your command handler code here
 shunxu=FALSE;
 SortItems(CompareFunc,(LPARAM)this);
}

即可完成排序功能!!!

 

 

CListCtrl学习笔记(4)---中级篇(3)

 

专题3:如何使列头响应右击消息?

我们知道在ClistCtrl中是可以响应列头单击的,如响应消息LVN_COLUMNCLICK,但是不能响应右击.怎么办呢?

(1)

实际上我们可以把CListCtrl(Report风格)看成由子窗口CHeaderCtrl和下面的视图组成,所以我们决定重写CHeaderCtrl.

派生一个类于CHeaderCtrl,响应右击:

void CMyHeadCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
 // TODO: Add your message handler code here and/or call default
 CHeaderCtrl::OnRButtonDown(nFlags, point);
 
}

在其中编写你想要的代码.这里完成了第一步.

 

(2)

此时还没有安装到ClistCtrl 上呢?想什么办法安装呢?以前在重写控件的时候,是通过在ClassWizard中定义变量来实现的,现在显然不行.因为ClistCtrl自己就是一个控件了.难道就没有办法了吗?

有.在CListCtrl中不是有个函数是GetHeaderCtrl吗?它不就是获得列头的指针吗?我们应该怎样利用它呢?

在想想子类化的方法:

如果我们重写了一个控件,如CMyEdit(派生于CEdit),现在要安装,除了在classwizard中安装以外,还可以在OnInitDialog中进行子类化:

.h:

CMyEdit m_edit;

.cpp:

m_edit.SubClassDlgItem(IDC_EDIT1,this);

 

(3) 回到我们这里的话题.显然不能用subclassDlgItem了,因为列头没有什么标识号,不过还可以用SubClassWindow,参数正好是一个句柄.

实现如下:

重写Cmylist(CListCtrl的派生类)虚函数:

void Cmylist::PreSubclassWindow()
{
 // TODO: Add your specialized code here and/or call the base class
 m_headctrl.SubclassWindow(GetHeaderCtrl()->GetSafeHwnd());
 CListCtrl::PreSubclassWindow();
}
安装成功!!!

 

CTreeCtrl学习笔记1--基础篇

 

以下的说明中,注意以下变量的定义:

 CTreeCtrl m_treectrl;//是关联变量

 

 

  1.如何获得选中项句柄?

HTREEITEM h=m_treectrl.GetSelectedItem();

 

2.如何获得右击项句柄?

 

void CLayerDialog::OnRclick(NMHDR* pNMHDR, LRESULT* pResult)

{

      // TODO: Add your control notification handler code here

      NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

      //右击获取所选项

      CPoint   point,p;  

      TVHITTESTINFO   HitTestInfo;  

      GetCursorPos(&point);

      m_treectrl.ScreenToClient(&point);  

      HitTestInfo.pt   =   point;  

      HTREEITEM h   =   m_treectrl.HitTest(&HitTestInfo);

      if(h!=NULL)

      {

             。。。。。//需要代码 

      }

}

3.如何响应checkbox被单击?

 

响应单击事件,并且进行判断

void CLayerDialog::OnLclick(NMHDR *pNMHDR,LRESULT *pResult)

{

   NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

   // TODO: Add your control notification handler code here

   CPoint p;

   GetCursorPos(&p);

   m_treectrl.ScreenToClient(&p);

   UINT nFlag;

   HTREEITEM h=m_treectrl.HitTest(p,&nFlag);

  if   ((h  !=   NULL)   &&   (TVHT_ONITEMSTATEICON  &   nFlag))  

   {   

          。。。。。//需要代码

    }

}

 

4.设置和获取checkbox的状态函数

   GetCheck( )              SetCheck( )

 

 

5.选中特定项

           HTREEITEM h=…;

           m_treectrl.SelecteItem(h);

 

    6.插入标记

这是拖曳时经常用到的函数。

BOOL SetInsertMark( HTREEITEMhItem, BOOLfAfter = TRUE );

TRUE表示在hItem下面显示横杠,而FALSE则表示在上面。

同类函数还有:

SetInsertMarkColorGetInsertMarkColor

 

    7 .获得兄弟姐妹项

HTREEITEMGetPrevSiblingItem( HTREEITEM hItem );

HTREEITEMGetNextSiblingItem( HTREEITEM hItem );

很好用的函数。但是要注意如果hItem是第一项,那么GetPrevSiblingItem返回NULL。此时要注意判断。GetNextSiblingItem同理。

 

 

 

CTreeCtrl学习笔记2-专题篇

 

如何对CTreeCtrl的项进行拖曳

 

1取消TVS_DISABLEDRAGDROP样式,该样式会禁止发送TVN_BEGINDRAG消息

 

 

2派生一个类于CTreeCtrl,定义几个成员变量:

   CImageList *m_pDragImage;//拖曳图像列表指针

   BOOL m_bLDragging;//是否拖曳

   HTREEITEM m_hitemDrag,m_hitemDrop;//拖曳项和目标项

 

 

3)响应拖曳消息TVN_BEGINDRAG

void CTreeCtrlEx::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)

{

   NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;

   // TODO: Add your control notification handler code here

   m_hitemDrag=pNMTreeView->itemNew.hItem;//获得拖曳项

   m_hitemDrop=NULL;

   m_pDragImage=CreateDragImage(m_hitemDrag);//创建拖曳图像

   if(!m_pDragImage)

          return;

   this->m_bLDragging=TRUE;

   m_pDragImage->BeginDrag(0,CPoint(-15,-15));//后面的point表示拖曳图像离拖曳项的相对位置

   POINT pt=pNMTreeView->ptDrag;

   ClientToScreen(&pt);

   m_pDragImage->DragEnter(NULL,pt);//开始显示拖曳图像

   SetCapture();//锁定鼠标

   *pResult = 0;

}

 

 

4)响应鼠标移动消息

void CTreeCtrlEx::OnMouseMove(UINT nFlags, CPoint point)

{

      // TODO: Add your message handler code here and/or call default

      HTREEITEM hitem;

      UINT flags;

      if(m_bLDragging)

      {

             POINT pt=point;

             ClientToScreen(&pt);

             CImageList::DragMove(pt);//当鼠标移动时也移动拖曳的图像

             if((hitem=HitTest(point,&flags))!=NULL)//确定鼠标所在的项

             {

                    CImageList::DragShowNolock(FALSE);//隐藏拖曳图像

                    SelectDropTarget(hitem);

                    m_hitemDrop=hitem;//确定目标项

                    CImageList::DragShowNolock(TRUE);//重新显示

             }

      }

 

      CTreeCtrl::OnMouseMove(nFlags, point);

}

 

 

5)鼠标弹起消息

void CTreeCtrlEx::OnLButtonUp(UINT nFlags, CPoint point)

{

      // TODO: Add your message handler code here and/or call default

      if(m_bLDragging)//是否拖动

      {

             m_bLDragging=FALSE;

             CImageList::DragLeave(this);

             CImageList::EndDrag();//结束拖曳

             ReleaseCapture();

             delete m_pDragImage;

             SelectDropTarget(NULL);

             if(m_hitemDrag==m_hitemDrop)

                    return;

      

             MessageBox("拖曳成功!");

。。。。现在已经获知了拖曳项和目标项,就看你自己需要什么代码了,略去。

      }

      CTreeCtrl::OnLButtonUp(nFlags, point);

}

 

 

CTreeCtrl学习笔记3--专题篇

如何给树控件加入工具提示

 


首先给树控件加入TVS_INFOTIP属性风格,如下所示:
if (!m_ctrlTree.Create(WS_CHILD|WS_VISIBLE|
TVS_HASLINES|TVS_HASBUTTONS|TVS_LINESATROOT|TVS_SHOWSELALWAYS|TVS_INFOTIP, //
加入提示TVS_INFOTIPjingzhou xu(树控件ID:100)
  CRect(0, 0, 0, 0), &m_wndTreeBar, 100))
  {
    TRACE0("Failed to create instant bar child\n");
     return -1;
  }

2其次加入映射消息声明,如下所示:
afx_msg void OnGetInfoTip(NMHDR* pNMHDR,LRESULT* pResult);       //
树控件上加入提示消息
ON_NOTIFY(TVN_GETINFOTIP, 100, OnGetInfoTip)                     //
树控件条目上加入提示
3最后加入呼应涵数处理:
void CCreateTreeDlg::OnGetInfoTip(NMHDR* pNMHDR, LRESULT* pResult) 
  {
  *pResult = 0;
  NMTVGETINFOTIP* pTVTipInfo = (NMTVGETINFOTIP*)pNMHDR;
  LPARAM itemData = (DWORD) pTVTipInfo->lParam;
  //
对应每个条目的数据
  HTREEITEM hItem = pTVTipInfo->hItem;
  CString tip;
  HTREEITEM hRootItem = m_chassisTree.GetRootItem();
  if (hRootItem != pTVTipInfo->hItem)
  {
    tip = "
树结点的提示";
  }
  else
  {
    tip = "
树根上的提示";
  }
  strcpy(pTVTipInfo->pszText, (LPCTSTR) tip);
 }

 

 

工具栏上快速添加文字

 

m_wndToolBar.SetButtonText(0," 控制 ");
 m_wndToolBar.SetButtonText(1," 编辑 ");
 /////////////调整工具条/////////////////
 CRect rc(0, 0, 0, 0);
 CSize sizeMax(0, 0);
 CToolBarCtrl& bar = m_wndToolBar.GetToolBarCtrl();
 //可能由于字数的不同,所以要找到一个图形最大的工具栏项
 for (int nIndex = bar.GetButtonCount() - 1; nIndex >= 0; nIndex--)
 {
  bar.GetItemRect(nIndex, rc);
  rc.NormalizeRect();
  sizeMax.cx = __max(rc.Size().cx, sizeMax.cx);
  sizeMax.cy = __max(rc.Size().cy, sizeMax.cy);
 }
 m_wndToolBar.SetSizes(sizeMax, CSize(16,15));

 

c中的陷阱

先看个题目:
void fun(char *p1)
{
p1=p1+1;}
main()
{
char *p="abcd";
fun(p);
cout<<*p;
}

这个题目看起来非常简单,但对于初学者来说却是个巨大的陷阱.很多初学者在学习c的时候就会听老师说过:指针是可以用来传值,甚至可以不要return;这句话本身没有错误,错的是初学者的理解!
首先说一下答案:结果是a不是b
这也许出乎你的意料.但是仔细一想就明白了.首先定义了一个字符型指针p,让它指向一个字符串的首地址,如果没有fun函数,那么*p='a';但是现在调用了fun,难道对p没有影响?
是的.调用fun的时候,会将p的值传给p1,也就是说现在p1也指向这个字符串的首地址了.然后执行:p1=p1+1;这样p1会指向下一个字符,也就是'b',但是p还是指向首字符!!!
所以结果也就出来了.这时候你可能会怀疑你以前学的指针是不是出现了问题.再来看这个题目,你就明白了:
void fun(char *p1)
{
*p1=*p1+2;
}
主函数不变,结果是'c'(a+2),同样p1开始指向了首地址,就当作一个箱子,然后把箱子里的东西换了(*p1),等调用结束以后,下个人(主函数)再来看的时候,箱子里的东西已经变化了!
总之,希望初学者在学习的时候多加琢磨一些关键的话语,这对你的分析能力是很有帮助的.
2.
main()
{
char p[]={'a','b','c'};
char q[]="abc";
printf("%d,%d,%d,%d",strlen(p),strlen(q),sizeof(p),sizeof(q));
}

感兴趣的读者,你能在不上机的情况下说出结果吗?
答案 是3,3,3,4.

我们知道strlen测的 是字符串的实际长度,不包括'\0',而sizeof测的是开始分配的空间.对于char q[]="abc";实际上应该是q[4],而对于char p[]={'a','b','c'};这种写法在很多C语言书上是不允许的,因为没有结束符.所以它的结果可能会出乎意料.在此不多作申明.不过如果写成char p[]={'a','b','c','\0'};那么sizeof(p)=4

3.new的问题

初学者都知道new是用来动态分配空间的,可以不用初始化,而直接赋值.如:

int *p=new int;

*p=5;//ok

但是

int *p;//只是一个地址变量,并不知道所指的空间

*p=5;//error

除了这些之外,你还知道关于它的什么吗?可能你还知道要释放.

那不释放会出现什么呢?

在<<c++程序设计教程>>(钱能著)的第14章有段很重要的话:

"c++程序的内存格局通常有4个区,

  • 全局代码区
  • 代码区
  • 栈区
  • 堆区

全局变量,静态数据,常量存放在全局数据区,所有类成员函数和非成员函数代码存放在代码区,为运行函数而分配的局部变量,函数参数,返回数据,返回地址,等存放在栈区,余下的空间都被作为堆区."

也就是说我们用new创建的空间是分配在堆区了,那堆区有什么性质呢?

"操作堆内存时,如果分配了内存,就有责任回收它,否则运行的程序会造成内存泄漏.但是在栈区,只有局部对象退出了作用域,系统自动回收.注意,对于类而言,堆对象的作用域是整个程序生命期,除非调用了delete,否则就不调用其析构函数."

如此说来,如果不用delete释放内存,就会造成内存泄漏,问题可就大了,但是有的时候并不是刚用new创建就要求析构.它的好处就是和整个程序同生命周期,那这样只要你保存了内存地址,就可以随时访问了.

最后再DELETE也不迟.

下面举出一个在MFC中非常经典的例子:

创建模态窗口与非模态是不同的,前者通常用如下代码:

Cmydlg dlg;

dlg.Domodal();

有人担心dlg是局部对象会被析构,并非如此,在执行到domodal时,程序会暂停执行,等待执行结果,传值给domodal,不过这里不是我要讲的主题.

再看看非模态窗口的创建过程:

(1)Cmydlg dlg;

dlg.Create(..);

dlg.ShowWindow();

这样的创建程序是会出错的,因为dlg获得是栈上内存,会自动析构,那关联的资源就没了.

(2)Cmydlg *dlg=new Cmydlg;

dlg->Create(..);

dlg->ShowWindow();

这种就是对的了.因为此时分配的是堆上内存,生命期为整个程序,所以不用担心内存被回收,也就不用担心窗口会显示不出了.

当然如果关闭了对话框那么这段内存就要回收了,可以用下面的代码:

void CMydlg::PostNcDestroy()

{

delete this;

}

 

 
 
 
 
原创粉丝点击