VC++中使用用户自定义消息及自定制窗口技巧

Windows 应用程序所要做的每项工作几乎都是基于消息处理的, Windows 系统消息分为常用 Windows 消息,控件通知消息和命令。然而,有时我们需要定义自己的消息来通知程序什么事情发生了,这就是用户自定义消息。 ClassWizard 并没有提供增加用户自定义消息的功能,所以要使用用户自定义消息,必须手工编写代码。然后 ClassWizard 才可以象处理其它消息一样处理你自定义的消息。具体做法如下详解:

   第一步:定义消息。一个消息实际上是开发 Windows95 应用程序时, Microsoft 推荐用户自定义消息至少是 WM_USER+100 ,因为很多新控件也要使用 WM_USER 消息。

   第二步:实现消息处理函数。该函数使用 WPRAM 和 LPARAM 参数并返回 LPESULT 。

   LPESULT CMainFrame::OnMyMessage(WPARAM wParam, LPARAM lParam){// TODO: 处理用户自定义消息 AfxMessageBox(" 处理用户自定义消息 "); return 0;}

   第三步:在类头文件的 AFX_MSG 块中说明消息处理函数:
   class CMainFrame:public CMDIFrameWnd{
   ...
   // 一般消息映射函数

   protected:

   // {{AFX_MSG(CMainFrame)

   afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);

   afx_msg void OnTimer(UINT nIDEvent);

   afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);

   //}}AFX_MSG

   DECLARE_MESSAGE_MAP()}

   第四步:在用户类的消息块中,使用 ON_MESSAGE 宏指令将消息映射到消息处理函数中。
   BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)

   //{{AFX_MSG_MAP(CMainFrame)

   ON_WM_CREATE()

   ON_WM_TIMER()

   ON_MESSAGE(WM_MY_MESSAGE, OnMyMessage)

   //}}AFX_MSG_MAPEND_MESSAGE_MAP()

   这样,一个用户自定义消息就可以使用了,如果用户需要一个整个系统唯一的消息,可以调用 SDK 函数 RegisterWindowMessage 并使用 ON_REGISTER_MESSAGE 宏指令取代 ON_MESSAGE 宏指令,其余步骤同上。

   VC++ 为程序员提供了一套功能强大、方便快捷的编程工具,它可以帮你方便的生成窗口、菜单等用户界面,可惜就是做出来的东西都一样,没有一点个性。下面,就介绍一些方法,让我们可以按照自己的设计定制出更加符合自己程序风格的窗口。

   一、如何在多文档界面下去掉开始的子窗口

   在多文档界面程序中,程序刚启动的时候会自动打开一个新的子窗口,而一个实际的应用系统往往是由用户操作后再生成新的窗口。下面是如何去掉开始的子窗口。

   首先在应用程序的 App 类里找到

   BOOL CMyMDIApp::InitInstance() 下面有:
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);

   这是在处理命令行参数,在这几句话后面加一行:

   cmdInfo.m_nShellCommand=CCommandLineInfo::FileNothing; 就可以了。再运行程序,就会发现程序并没有自动开启一个子窗口,而只剩下主框架窗口了。

   二、修改窗口标题栏

   在缺省情况下,窗口标题栏中显示的标题为程序名 + 当前文档的文件名。比如 " MyProgram - 文档 1 . t x t " ,那若要在标题栏显示一个自己定义的字符串,而不是程序名,可以通过在程序里调用 CWnd::SetWindowText() 方法来实现,而如果我们还想要后面的文档名自动显示,这么做就不行了,这时可以用资源编辑器编辑字符串表( StringTable )资源,在 StringTable 中双击 IDR-MAIN-FRAME 项, caption 中显示一字符串 xx\n\yy...... ,将第一个参数修改为用户自己希望见到的主窗口标题即可。

   如果你不想让系统自动帮你把文档的文件名添加到标题栏中,需要在 CMainFram 的 PreCreateWindow 函数中删除 FWS_ADDTOTITLE 标志的窗口样式:

   cs.style &= ~FWS_ADDTOTITLE ;

   这样,程序运行起来,窗口标题就是 "MyProgram" 而没有后面的 "- 文档 1.txt" 这样的字符串了。

   三、修改主框架窗口、子窗口及其显示风格

   MFC 的 CWnd 类会在调用 CWnd::Create() 方法前先调用一下 PreCreateWindo() 方法,其参数是 CREATESTRUCT cs ,其中包括了创建窗口时各参数,例如大小,风格等等,我们可以通过重载这个成员函数来修改主窗口和子窗口的风格等属性。 PreCreateWindow 函数的原型为: Virtual BOOL PreCreateWindow ( CREATESTRUCT cs )。重载 PreCreateWindow 函数以后,则在创建窗口前可以修改 CREATESTRUCT 结构以替换缺省参数。 CREATESTRUCT 结构存放窗口特征,如窗口坐标、风格等,还可以定义新窗口风格。

   若想修改主框架窗口,则可以在 MainFrm.cpp 的下列成员函数中加入待修改的内容。例如:
   BOOL CMainFrame::PreCreateWindow(CREATESTRUCT&cs)
{
// 通过修改 CREATESTRUCT 结构来修改窗口类或风格
cs.cx=450;
cs.cy=300;
// 定义新窗口的高度、宽度
// 定义新窗口风格为去掉主窗口名及最大化等按钮
cs.style=ws-POPWINDO;
return CFrameWnd::PreCreateWindow(cs); }

   定制子窗口的操作与上述主窗口相同,可在 ChildFrame.cpp 中加入以下内容:

   BOOL CMainFrame::PreCreateWindow(CREATESTRUCT&cs)
{
// 通过修改 CREATESTRUCT 结构来修改窗口类或风格
return CMDIChildWnd::PreCreateWindow(cs);
}

  要修改视图窗口的显示性质,则可在视图文件 MyView.cpp 的下述成员函数中加入以下语句:
   BOOL MyView::PreCreateWindow(CREATESTRUCT&cs)
{
// 在这里修改 cs 结构,改变 View 的风格。
cs.lpszClass=AfxRegisterWndClass(cs-HREDRAW|CS-VREDRAW,0,(HBRUSH))::GetStockObject(WHITE-BRUSH),0);
return CScrollView::PreCreateWindow(cs);
}

   其中, cs 的参数 pszClass 用于存放 Windows 窗口类名称。要想注册 Windows 窗口类,则必须调用全局函数 AfxRegisterWndClass 。该函数原型为:
   LPCTSTR AFXAPI AfxRegisterWndClass(UINTnClassStyle,HCURSOR hCursor=0,HBRUSH hbrBackground=0,HICON hIcon=0)

   上述各参数用于定义风格,其含义分别为光标资源句柄、背景资源句柄、图标资源句柄。上述增加的语句的作用是:改变窗口大小时重画窗口、不显示光标图标、设置白色背景。

   四、窗口的滚动

   MFC 中的 CScrollView 可以帮助你自动实现窗口滚动的决大部分功能,使用 CscrollView 时, ClassWizard 生成 OnInitialUpdate() 成员函数为:
   void CMyScrollView::OnInitialUpdat()
{
CScrollView::OnIntialUpdate();
CSize sizePage;
sizePage.cs=sizePage.cy=400;
SetScrollSizes(MM-TEXT,sizePage);
}

   其中, cs 和 cy 分别为滚动窗口的水平、垂直分量,表明窗口的水平、垂直方向尺寸小于 400 像素单位时将出现水平方向滚动条和垂直方向滚动条。通过修改滚动尺寸,可改变出现滚动条的最小窗口。例如,若 sizePage.cx=600;sizePage.cy=800; ,则当窗口尺寸小于 600*800 时,就会出现滚动条。

   五、窗口分割

   该功能可将窗口分割成多个可滚动的帧,帧之间的边界称为分割条,可用分割条来调整每个帧的相对大小。要想增加窗口分割功能,则必须修改主窗口类。首先,在主窗口类的头文件 MainFrm.h 中添加以下代码:
   CSplitterWnd m-SWnd;
Virtual BOOL OnCreateClient (LPCREATESTRUCTcs,CcreateContext *pContext);

   再在 MainFrm.cpp 中添加成员函数 OnCreateClient 的定义:
   BOOL CmainFrame::OnCreateCline(LPCREATESTRUCTcs,CcreateContext *p Context)
{
return m-SWnd.Creat(this,2,2,Csize(20,20),pContext);
}

   新的 CSplitterWnd 类对象 m-SWnd 用于创建和管理分割窗口,该窗口中可以包含一个或多个帧。首次创建主窗口时,将调用成员函数 OnCreateClient 。在缺省情况下,该函数创建一个填充主框窗口客户区的视图窗口。覆盖该函数后,将调用 CsplitterWnd 的成员函数 Create 来创建分割窗口。其中,第一个参数用于指定分割的父窗口(主窗口);第二个参数指定垂直方向上的帧个数为 2 ;第三个参数指定水平方向上的帧的个数;第四个参数用于设置每个帧的最小尺寸;第五个参数传递描述信息。上述分割窗口的每个帧都是由视图类对象管理的,当用户在某一帧内显示文档和图形时,必须在其它帧中重新绘制,从而在多个帧中均显示相同的内容。为此,必须调用显示文档类的 UpdateALLView 成员函数来更新其它帧。此时,只需加入 pdoc- > UpdataALLView(NULL) 即可。

posted on 2008-04-08 19:35 wrh 阅读(2129) 评论(5)  编辑 收藏 引用

评论

# re: VC++中使用用户自定义消息及自定制窗口技巧 2008-05-20 11:49 n

不好意思,虽然很唐突,想要请教您几个问题。
我最近才开始因为毕设的关系接触MFC,现在用MDI框架作了一个程序,搜索图片的。现在有个问题:
我想要在一个按键的事件响应里,新开一个窗口,绘制我查找到的图片,现在图片路径找到了,但是由于MDI很复杂,我不知道怎么调用OnDraw()重绘这幅图。。。现在很困扰,看了您的自定制窗口技巧,觉得和我的问题有些沾边,所以冒昧给您发邮件,希望您能帮帮我,不胜感激。  回复  更多评论   

# re: VC++中使用用户自定义消息及自定制窗口技巧 2008-05-21 21:38 wrh

给你发过去了,你是做图像检索的吗??可以交流一下,  回复  更多评论   

# re: VC++中使用用户自定义消息及自定制窗口技巧 2008-12-25 13:51 qiaoyusun

看了你的帖子,很有收获,也很好用。我是做图像处理和模式识别的,因为刚开始上手,被MFC的机制弄得无比烦恼。现在想请教一个问题:假设要对一幅图像做边缘检测,很多程序都是在原视图中直接修改然后你可以点另存。我希望重新打开一个子窗口显示检测后的图,而原图还保留在原来的子窗口中。不知该怎么做。如果能得到您的帮助那就太感谢了。  回复  更多评论   

# re: VC++中使用用户自定义消息及自定制窗口技巧 2008-12-30 09:47 wrh

@qiaoyusun
有两种方式,一种是将视图分成两个视图,左和右,左边视图显示原图,右边视图显示处理后的图像,将处理函数加到右边视图中,处理完后更新视图就行了。另一种方式是建成多文档的,每处理完后都在新文档中打开显示。建议你用第一种。
  回复  更多评论   

# re: VC++中使用用户自定义消息及自定制窗口技巧 2008-12-31 08:39 qiaoyusun

多谢你的回复!!!
因为要看到四个不同方向的边缘检测结果,我用的是后一种方法。我已经打开了新文档,不过处理结果显示不出来。我有个例子是新建一个DIB对象将原图的头部信息拷过来,加上处理好的数据显示:
POSITION posTemplate = pApp->GetFirstDocTemplatePosition();
CDocTemplate* pDocTemplate = pApp->GetNextDocTemplate(posTemplate);

CDipDoc* pDocument = (CDipDoc*) pDocTemplate->OpenDocumentFile(NULL);
pDocument->m_pDibObject = green;//green就是一个DIB对象
pDocument->m_bImageLoaded = TRUE;
pDocument->SetTitle("*****");

//获取子框架窗口指针
CChildFrame *pChild = (CChildFrame *) pFrame->MDIGetActive();
pChild->m_nWidth = m_pDibObject->GetWidth();
pChild->m_nHeight = m_pDibObject->GetHeight();
pChild->SetWindowPos( NULL, 0, 0, pChild->m_nWidth + 12,
pChild->m_nHeight + 38, SWP_NOZORDER | SWP_NOMOVE );

pDocument->UpdateAllViews(NULL);
和我的DIB定义有很大区别,所以我始终不能将这个新的DIB对象构建好。
再次表示感谢!  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2008年4月>
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

统计

常用链接

留言簿(19)

随笔档案

文章档案

收藏夹

搜索

最新评论

阅读排行榜

评论排行榜