[转]MFC学习总结(五)

http://www.cnblogs.com/cy163/archive/2007/06/27/532871.html

51. 如何获得其他程序的图标,并显示在View中

 [问题提出]
 有的时候,如:类资源管理器会遇到获得程序图标并显示的操作,如何实现呢?
 [解决方法]
 SDK函数SHGetFileInfo来获得有关文件的很多信息:如大小图标,属性,类型等.
 [程序实现]
 建立名为My的SDI工程.在OnPaint()函数中加入:
 void CMyView::OnPaint()
 {
  CPaintDC dc(this); // device context for painting
  HICON hIcon=:: ExtractIcon(AfxGetInstanceHandle(),_T("NotePad.exe"),0);
  if (hIcon && hIcon!=(HICON)-1)
  dc.DrawIcon(10,10,hIcon);

  // TODO: Add your message handler code here
  // Do not call CView::OnPaint() for painting messages
 }
 说明:_T("NotePad.exe")指的是要获得什么程序的图标.
 或者在OnDraw()中(此时必须保证没有OnPaint()函数,想想为何?)
 void CMyView::OnDraw(CDC* pDC)
 {
  CMyDoc* pDoc = GetDocument();
  ASSERT_VALID(pDoc);
  // TODO: add draw code for native data here
  HICON hIcon=:: ExtractIcon(AfxGetInstanceHandle(),_T("NotePad.exe"),0);
  if (hIcon &&hIcon!=(HICON)-1)
  pDC->DrawIcon(10,10,hIcon);
 }

52 .RichEdit
在Dialog(FormView中打开)中加入CRichEdit控件后,这个dialog 为什么打不开如何处理?
 [解决方法]
 在函数:InitInstance的第一句加入AfxInitRichEdit();

53. 如何使FormView中显示dialog时,不是凹的?

 [问题提出]
 为什么FormView中显示dialog时,是凹的,能不能不这样
 [解决方法]
 在Dialog的属性中:
  增加属性WS_BORDER  或者 WS_EX_WINDOWEDGE
 用程序实现:
 pView->ModifyStyle(,WS_BORDER) 或者pView->ModifyStyleEx(,WS_EX_WINDOWEDGE )

54. 如何改变窗口标题?

 [问题提出]
  在应用程序的不同运行时期,要反映当前状态往往会修改应用程序标题.

 [解决方法]
  在MFC类库中提供了CWnd::SetWindowText函数,通过该函数可以改变任何窗体(包括控件)的标题.
  改变主窗体的标题:
  CWnd *m_pMainWnd;
  m_pMainWnd=AfxGetMainWnd();
  m_pMainWnd->SetWindowText(_T("改变标题"));
  当改变多视MDI的子窗口的标题时,用:
  GetParentFrame()->SetWindowText(_T("MDI Child改变标题"));
  当改变按钮的标题时(假设按钮的ID=IDC_BUTTON1):
  GetDlgItem(IDC_BUTTON1)->SetWindowText(_T("Button 改变标题"));
  运行看看.

55.图标透明

(1).Windows中的图标其实是有两个图像组成的,其中一个用于与它要显示的位置的图像做“AND”操作,另一个作“XOR”操作。
透明:用“白色”AND,用“黑色”XOR
反色:用“白色”AND,用“白色”XOR
正常色:用“黑色”AND,用正常颜色XOR.
(2). WIN9X中好像是对像素的操作实现透明的
WIN2K中就有API直接实现透明了!
WIN2K中
GetWindowLong
SetWindowLong
SetLayeredWindowAttributes
三个API就可以实现透明了!
(3)
::DrawIconEx(pDC->GetSafeHdc(),point.x,point.y,icon,icosize,icosize,0,NULL,DI_NORMAL);

56.ASSERT()是干什么用的

ASSERT()是一个调试程序时经常使用的宏,在程序运行时它计算括号内的表达式,如果表达式为FALSE (0), 程序将报告错误,并终止执行。如果表达式不为0,则继续执行后面的语句。这个宏通常原来判断程序中是否出现了明显非法的数据,如果出现了终止程序以免导致严重后果,同时也便于查找错误。例如,变量n在程序中不应该为0,如果为0可能导致错误,你可以这样写程序:
  ......
  ASSERT( n != 0);
  k = 10/ n;
  ......
  ASSERT只有在Debug版本中才有效,如果编译为Release版本则被忽略。
  assert()的功能类似,它是ANSI C标准中规定的函数,它与ASSERT的一个重要区别是可以用在Release版本中。

56. 将RADIO控件初始状态设置成为选中

1、"在OnInitialDialog中用CButton::CheckRadioButton(...)
2、"在OnInitialDialog中用CButton::SetCheck(...)
3、"关联一个整型值,在构造函数中设为0。

57.获得视图

CFrameWnd* pFrameWnd = (CFrameWnd*)theApp.GetMainWnd();
CMyView* pView = (CMyView*)pFrameWnd->GetActiveView();

58.如何得到屏幕的真实尺寸

[问题提出]
我的屏幕是1024*800,如何得到屏幕的真实大小,我用GetSystemMetrics(SM_CYFULLSCREEN)得到的高度总是小于800
[问题解答]
GetSystemMetrics(SM_CYFULLSCREEN)得到的只是屏幕用户区的大小。要得到屏幕的真实大小需要使用
GetDeviceCaps函数,该API函数原型是这样的:

int GetDeviceCaps(
 HDC hdc,   // handle to DC
 int nIndex  // index of capability
);
///得到屏幕尺寸的代码如下
void CMyDlg::OnPaint()
{
  CPaintDC dc(this);
  int cx = ::GetDeviceCaps(dc.m_hDC,HORZRES);///得到宽度
  int cy = ::GetDeviceCaps(dc.m_hDC,VERTRES);///得到高度
  CDialog::OnPaint();

59. 修改标题栏高度

NONCLIENTMETRICS nm
调用SystemParametersInfo(SPI_GETNONCLIENTMETRICS,sizeof(nm),&nm,0)
重设SystemParametersInfo(SPI_SETNONCLIENTMETRICS,sizeof(nm),&nm,0)

60. 如何实现“气球式”工具提示。

本程序介绍一个与CToolTipCtrl相似的类CTooolTipWnd。
使用该类的方法如下:
1. 增加ToolTipWnd.cpp到工程文件。
2. 在头文件中添加#include "ToolTipWnd.h" 。 
3. 在类声明中添加:
CToolTipWnd m_BalloonToolTip;
4. 在OnInitDialog(对话框)或OnInitialUpdate(表单视)中添加下面代码:
  m_BalloonToolTip.Create(this);
  m_BalloonToolTip.AddTool(GetDlgItem(), , [text color]);
eg.
  m_BalloonToolTip.AddTool(GetDlgItem(IDC_EDIT1),"Tooltip", RGB(255,0,0));
  第三个参数为可选,缺省为RGB(0, 0, 0)。缺省文本颜色可以用SetDefTextColor进行设置。
4. 重载PreTranslateMessage并添加下面代码:
  if(m_BalloonToolTip) 
    m_BalloonToolTip.RelayEvent(pMsg);


 

61. dlg 上建立View的方法:

OnInitDialog()
{
 CDialog:;OnInitDialog();

CRect rectWindows;
GetWinodwRect(&rectWindows);
CRuntimeClass *pViewClass=RUNTIME_CLASS(CXXXView);
CCreateContext *pContext=new CCreateContext;
pContext->m_pCurrentDoc=NULL;
pContext->m_pCurrentFrame=NULL;
pContext->m_pLastView=NULL;
pContext->m_pNewDocTemplate=NULL;
pContext->m_pNewViewClass=pViewClass;

CWnd *pWnd=DYNAMIC_DOWNCAST(CWnd,pviewClass->CreateObject());
pWnd->Create(NULL,NULL,AFX_WS_DEFAULT_VIEW,CRect(0,0,0,0),this,pContext);
delete pContext;
CXXXView *pView=DYUNAMIC_DOWNCAST(CXXXView,pWnd);
...............
}

62. 窗口最大化、最小化及关闭的消息是什么?如何截获?

最大化、最小化将发送WM_SYSCOMMAND消息。要处理该消息,可以这么做:
  1、在Form的头文件中添加:
   void __fastcall RestrictMinimizeMaximize(TMessage &Msg);
  
   BEGIN_MESSAGE_MAP
   MESSAGE_HANDLER(WM_SYSCOMMAND, TMessage, RestrictMinimizeMaximize)
   END_MESSAGE_MAP(TForm)
  2、在Form的单元文件中添加:
  
   void __fastcall TForm1::RestrictMinimizeMaximize(TMessage& Msg)
   {
   if (Msg.WParam == SC_MINIMIZE)
   {
   //catches minimize...
   }
   else if (Msg.WParam == SC_MAXIMIZE)
   {
   //catches maximize...
   }
   TForm::Dispatch(&Msg);
   // or "else TForm::Dispatch(&Msg)" to trap
   }
  关闭窗口的消息为WM_CLOSE,C++Builder提供了OnClose事件。

63. 如何遍历整个目录树查找文件
  
在应用程序的开发过程中,会遇到如何查找某一文件以确定此文件路径的问题。利用CFileFind类可以比较方便地在当前目录下进行文件查找,但却不能对其子目录中的文件进行搜寻。而实际应用中往往需要对某一整个目录树,甚至是整个C盘或D盘驱动器进行文件搜寻。通过实践,我们在Visual C++ 6.0中编程实现了如何遍历任意目录树,以查找某一特定的文件。
  在下面的具体陈述中可以看到,在确定要查找的文件名和要进行搜索的目录的名称后,将调用函数Search_Directory进行文件的查找。首先依次查找当前目录下的每一个实体(文件或是子目录),如果是某一子目录,则进入该子目录并递归调用函数Search_Dirctory进行查找,查找完毕之后, 再返回上一级目录;如果不是子目录而是某一文件,则判断其是否就是我们要查找的文件,如果是则输出其完整的文件路径。这样,通过Search_Directory函数的反复递归调用,就可以实现对整个目录,包括子目录的遍历搜索。下面将举例详细讲述如何在VC++中编程实现在整个目录树中的文件查找。
  1. 在Visual C++ 6.0(VC++ 5.0与之类似)中用默认方式创建了一基于对话框的应用程序Search。在主窗口对话框上放置一命令按钮,其Caption为“Search File”,ID为ID_BUTTON_SEARCH。单击此按钮将完成文件的查找工作。
  2. 利用ClassWizard为“Search File”按钮的BN_CLICKED 事件添加处理函数OnButtonSearch,代码如下:

#include 〈direct.h〉
#include 〈io.h〉
void CSearchDlg::OnButtonSearch()
{
  // TODO: Add your control notification handler code here
 
  char szFilename[80];
  // 字符串 szFilename 表示要查找的文件名

  strcpy(szFilename,"Mytext.txt");

  _chdir("d://"); // 进入要查找的路径(也可为某一具体的目录)
  // 查找文件, 如果查到则显示文件的路径全名
  Search_Directory(szFilename);
  // 为CSearchDlg类的一成员函数
  MessageBox(″查找文件完毕!″);
  // 显示查找完毕的信息
}


  3. 在CSearchDlg类中增加成员函数Search_Directory,它将完成具体的文件查找工作,代码如下:
void CSearchDlg::Search_Directory(char* szFilename)
{
  long handle;
  struct _finddata_t filestruct;
  //表示文件(或目录)的信息
  char path_search[_MAX_PATH];
  //表示查找到的路径结果
  // 开始查找工作, 找到当前目录下的第一个实体(文件或子目录),
  // "*"表示查找任何的文件或子目录, filestruct为查找结果
  handle = _findfirst("*", &filestruct);
  // 如果handle为-1, 表示当前目录为空, 则结束查找而返回
  if((handle == -1)) return;
  // 检查找到的第一个实体是否是一个目录(filestruct.name为其名称)
  if( ::GetFileAttributes(filestruct.name) & FILE_ATTRIBUTE_DIRECTORY )
  {
   // 如果是目录, 则进入该目录并递归调用函数Search_Dirctory进行查找,
   // 注意: 如果目录名的首字符为'.'(即为"."或".."), 则不用进行查找
   if( filestruct.name[0] != '.' )
   {
     _chdir(filestruct.name);
     Search_Directory(szFilename);
     // 查找完毕之后, 返回上一级目录
     _chdir("..");
   }
  }
  else // 如果第一个实体不是目录, 则检查是否是要查找的文件
  {
   // stricmp对两字符串进行小写形式的对比, 返回为0表示完全一致
   if( !stricmp(filestruct.name, szFilename) )
   {
     // 先获得当前工作目录的全路径
     _getcwd(path_search,_MAX_PATH);
     // 再获得文件的完整的路径名(包含文件的名称)
     strcat(path_search,"//");
     strcat(path_search,filestruct.name);
     MessageBox(path_search); //输出显示
   }
  }
  // 继续对当前目录中的下一个子目录或文件进行与上面同样的查找
  while(!(_findnext(handle,&filestruct)))
  {
   if( ::GetFileAttributes(filestruct.name) & FILE_ATTRIBUTE_DIRECTORY )
   {
     if(*filestruct.name != '.')
     {
      _chdir(filestruct.name);
      Search_Directory(szFilename);
      _chdir("..");
     }
   }
   else
   {
     if(!stricmp(filestruct.name,szFilename))
     {
      _getcwd(path_search,_MAX_PATH);
      strcat(path_search,"//");
      strcat(path_search,filestruct.name);
      MessageBox(path_search);
     }
   }
  }
  _findclose(handle);
  // 最后结束整个查找工作
}
  这样我们就可以对整个目录进行遍历搜索,查找某一特定的文件,并输出显示其完整的文件路径。以上的程序在Visual C++ 6.0中已调试通过。

64. Richedit control的设置背景图片办法

1:继承CRichEditCtrl::OnEraseBkgnd(CDC* pDC)消息事件中,给Richedit控件绘制上背景图片:m_bmpBackground.DrawDIB(pDC, 0, 0, rc.Width(), rc.Height());当然也可以通过subclass richedit window之后,在回调函数中处理WM_ERASEBKGND消息。
2:设置了Richedit控件的透明属性;
3:依照kenwhale所说的,Hook了GDI32.DLL中的ExtTextOut函数,将RichEdit的text-output options去除ETO_OPAQUE style
。综上所述,即可实现RichEdit控件的背景图片效果。
据此,我还实现了RichEdit控件背景绘制AVI动画效果。

65. MFC程序中如何创建多级目录

BOOL mkdirEx(const char* lpPath)
{
CString pathname = lpPath;
if(pathname.Right(1) != "/")
pathname += "/" ;
int end = pathname.ReverseFind('/');
int pt = pathname.Find('/');
if (pathname[pt-1] == ':')
pt = pathname.Find('/', pt+1);
CString path;
while(pt != -1 && pt<=end)
{
path = pathname.Left(pt+1);
if(_access(path, 0) == -1)
_mkdir(path);
pt = pathname.Find('/', pt+1);
}
return true;
}

66. 解决外部符号错误:_main,_WinMain@16,__beginthreadex

在创建MFC项目时, 不使用MFC AppWizard向导, 如果没有设置好项目参数, 就会在编译时产生很多连接错误, 如error LNK2001错误, 典型的错误提示有:
libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16
msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex
下面介绍解决的方法:
1). Windows子系统设置错误, 提示:
libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
Windows项目要使用Windows子系统, 而不是Console, 可以这样设置:
[Project] --> [Settings] --> 选择"Link"属性页,
在Project Options中将/subsystem:console改成/subsystem:windows
2). Console子系统设置错误, 提示:
LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16
控制台项目要使用Console子系统, 而不是Windows, 设置:
[Project] --> [Settings] --> 选择"Link"属性页,
在Project Options中将/subsystem:windows改成/subsystem:console
3). 程序入口设置错误, 提示:
msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16
通常, MFC项目的程序入口函数是WinMain, 如果编译项目的Unicode版本, 程序入口必须改为wWinMainCRTStartup, 所以需要重新设置程序入口:
[Project] --> [Settings] --> 选择"C/C++"属性页,
在Category中选择Output,
再在Entry-point symbol中填入wWinMainCRTStartup, 即可
4). 线程运行时库设置错误, 提示:
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex
这是因为MFC要使用多线程时库, 需要更改设置:
[Project] --> [Settings] --> 选择"C/C++"属性页,
在Category中选择Code Generation,
再在Use run-time library中选择Debug Multithreaded或者multithreaded
其中,
Single-Threaded单线程静态链接库(release版本)
Multithreaded多线程静态链接库(release版本)
multithreaded DLL多线程动态链接库(release版本)
Debug Single-Threaded单线程静态链接库(debug版本)
Debug Multithreaded多线程静态链接库(debug版本)
Debug Multithreaded DLL多线程动态链接库(debug版本)
单线程: 不需要多线程调用时, 多用在DOS环境下
多线程: 可以并发运行
静态库: 直接将库与程序Link, 可以脱离MFC库运行
动态库: 需要相应的DLL动态库, 程序才能运行
release版本: 正式发布时使用
debug版本: 调试阶段使用
67. 创建包含多个子目录的目录
void CreateAllDirectories(CString strDir)
{
//remove ending / if exists
if(strDir.Right(1)=="//")
 strDir=strDir.Left(strDir.GetLength()-1);

// base case . . .if directory exists
if(GetFileAttributes(strDir)!=-1)
 return;

// recursive call, one less directory
int nFound = strDir.ReverseFind('//');
CreateAllDirectories(strDir.Left(nFound));

// actual work
CreateDirectory(strDir,NULL);
}
68. ReverseFind()
#include
#include
int main()
{
  CString s;
  s.Format("abcdefghijk");
  int nPos = s.ReverseFind('a');
  printf("nPos is %d/n",nPos);
  return 0;
}
其中,'a'对应的nPos是0,'h'对应的nPos是7,以此类推。但是:s.ReverseFind('a')和s.Find('a')的结果是一样的。
问题是:ReverseFind() 和 Find() 有什么区别呢:
对于ReverseFind(),查找顺序是从后往前,找到后的nPos是按前后顺序排列的。
而Find()是从前往后查的,找到后的nPos也是按前后顺序排列的。
69. MDI中如何只屏蔽掉子框架的右上角的关闭按钮
int CChildFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  if (CMDIChildWnd::OnCreate(lpCreateStruct) == -1)
    return -1;
。。。
  CMenu* pSysMenu = GetSystemMenu(FALSE);
  pSysMenu->EnableMenuItem(SC_CLOSE,MF_BYCOMMAND |MF_DISABLED|MF_GRAYED);
  return 0;
}
70. 程序如何删除自己
/////////////////////////////////////////////////
 
int WINAPI WinMain(HINSTANCE h, HINSTANCE b, LPSTR psz, int n) {
 
// Is this the Original EXE or the clone EXE?
// If the command-line 1 argument, this is the Original EXE
// If the command-line >1 argument, this is the clone EXE
 
if (__argc == 1) {
 
// Original EXE: Spawn clone EXE to delete this EXE
// Copy this EXEcutable image into the user''s temp directory
 
TCHAR szPathOrig[_MAX_PATH], szPathClone[_MAX_PATH];
GetModuleFileName(NULL, szPathOrig, _MAX_PATH);
GetTempPath(_MAX_PATH, szPathClone);
GetTempFileName(szPathClone, __TEXT("Del"), 0, szPathClone);
CopyFile(szPathOrig, szPathClone, FALSE);
 
//***注意了***:
// Open the clone EXE using FILE_FLAG_DELETE_ON_CLOSE
HANDLE hfile = CreateFile(szPathClone, 0, FILE_SHARE_READ, NULL, OPEN_EXISTI
NG, FILE_FLAG_DELETE_ON_CLOSE, NULL);
 
// Spawn the clone EXE passing it our EXE''s process handle
// and the full path name to the Original EXE file.
TCHAR szCmdLine[512];
HANDLE hProcessOrig = OpenProcess(SYNCHRONIZE, TRUE, GetCurrentProcessId());

wsprintf(szCmdLine, __TEXT("%s %d /"%s/""), szPathClone, hProcessOrig, szPat
hOrig);
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
PROCESS_INFORMATION pi;
CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
CloseHandle(hProcessOrig);
CloseHandle(hfile);
 
// This original process can now terminate.
} else {
// Clone EXE: When original EXE terminates, delete it
HANDLE hProcessOrig = (HANDLE) _ttoi(__targv[1]);
WaitForSingleObject(hProcessOrig, INFINITE);
CloseHandle(hProcessOrig);
DeleteFile(__targv[2]);
// Insert code here to remove the subdirectory too (if desired).
 
// The system will delete the clone EXE automatically
// because it was opened with FILE_FLAG_DELETE_ON_CLOSE
}
return(0);
}
  这一段程序思路很简单:不是不能在运行时直接删除本身吗?好,那么程序先复制(CLONE)一个自己,用复制品起动另一个进程,然后自己结束运行,则原来的EXE文件不被系统保护.这时由新进程作为杀手删除原来的EXE文件,并且继续完成程序其他的功能。

  新进程在运行结束后,复制品被自动删除。这又是值得介绍的一个把戏了,注意:

// Open the clone EXE using FILE_FLAG_DELETE_ON_CLOSE
HANDLE hfile = CreateFile(szPathClone, 0, FILE_SHARE_READ, NULL,OPEN_EXISTIN
G, FILE_FLAG_DELETE_ON_CLOSE, NULL);
  这里面的FILE_FLAG_DELETE_ON_CLOSE标志,这个标志是告诉操作系统,当和这个文件相关的所有句柄都被关闭之后(包括上面这个CREATEFILE创建的句炳),就把这个文件删除。几乎所有的临时文件在创建时,都指明了这个标志。另外要注意的是:在复制品进程对原始程序操刀之前,应该等待原进程退出.在这里用的是进程同步技术.用HANDLE hProcessOrig = OpenProcess(SYNCHRONIZE, TRUE,GetCurrentProcessId());得到原进程句柄.SYNCHRONICE标志在NT下有效,作用是使OpenProcess得到的句柄可以做为同步对象.复制品进程用WaitForSingleObject函数进行同步,然后一个DeleteFile,以及进行其它销毁证据(比如删目录)的工作,一切就完事了。
 
  程序是基于CONSOLE的,通过传入的参数确定是原始的进程还是复制品新进程,并且得到需要操作的目标文件的信息(主要是路径),复制品放在系统的TEMP目录(GetTempPath得到),你也可以随便找个你认为安全的地方(比如:WINDOWS/SYSTEM32等等)。这里面没有甚么深的技术.再看其他的一些实现删除自己的例子,比如说在进程退出前,用fwrite等方法输出一个.BAT文件,在里面写几句DEL,然后WINEXEC一下这个BAT文件即可.玩儿过DOS的虫虫大多都会。

 

71. 隐藏标题栏和菜单栏
隐藏标题栏 ModifyStyle(WS_CAPTION,0)
隐藏菜单栏 SetMenu(NULL)
72. InflateRect
InflateRect这个函数用于增大或减小一个矩形的大小.
如m_graphRect.InflateRect(-70, -30, -30, -50);
将矩形左边坐标加70,上面加30,右边减30,下面减50。
73. 怎么让无模式对话框显示在主窗口后面
要解决这个问题的关键在于CDialog的Create并不能建立一个无属主的窗口.必须用另外方式建窗口. 
  
 比如你的对话框类叫CDlgNoOwner,在CMainFrame中加一个CDlgNoOwner类的成员变量, 
 弹出这个对话框的消息处理函数为 
  
 void  CMainFrame::OnNoowner()  
 { 
CDlgNoOwner  *m_dlgTest=new  CDlgNoOwner(this); 
 HWND  hwndDlg=::CreateDialog(AfxGetInstanceHandle(),MAKEINTRESOURCE(CDlgNoOwner::IDD),NULL/*owner*/,NULL/*dlgproc*/); 
 //注意此处DLGPROC为NULL,并不要紧,因为接下要subclass啦 
 m_dlgTest->SubclassWindow  (hwndDlg);//挂接到成员变量! 
 m_dlgTest->ShowWindow  (SW_SHOW); 
 //这时可以看到一个"自由"的对话框弹出,和你的主窗口是平起平坐的. 
 } 
  
 当然不要忘了在对话框关闭时DestroyWindow()..那都是在对话框类中的标准处理了.
74. 隐藏窗口(子窗口没有焦点时)

在程序启动时  InitDialog  中使用  SetWindowPos  将窗体设置到屏幕以外
然后再隐藏
1.在OnInitDialog()函数里设置定时器:(WINDOWS  API里面响应消息WM_INITDIALOG) 
  
 SetTimer(1,  1,  NULL); 
  
 2.添加处理WM_TIMER的消息处理函数OnTimer,添加代码: 
  
 if(nIDEvent  ==  1) 
  
 { 
  
 DeleteTimer(1); 
  
 ShowWindow(SW_HIDE); 
  
 } 
75.修改视图背景
How do I change the background color of a view?

To change the background color for a CView, CFrameWnd, or CWnd object, process the WM_ERASEBKGND message. The following code shows how:

BOOL CSampleView::OnEraseBkgnd(CDC* pDC)
{
  // Set brush to desired background color.
  CBrush backBrush(RGB(255, 128, 128));
  // Save old brush.
  CBrush* pOldBrush = pDC->SelectObject(&backBrush);
  CRect rect;
  pDC->GetClipBox(&rect);   // Erase the area needed.
  pDC->PatBlt(rect.left, rect.top, rect.Width(),
  rect.Height(), PATCOPY);
  pDC->SelectObject(pOldBrush);
  return TRUE;
}

I solved the problem like this:

HBRUSH dlgtest::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
  switch (nCtlColor)
  {
    case CTLCOLOR_BTN:
    case CTLCOLOR_STATIC:
    {
      pDC->SetBkMode(TRANSPARENT);
    }
    case CTLCOLOR_DLG:
    {
      CBrush*   back_brush;
      COLORREF  color;
      color = (COLORREF) GetSysColor(COLOR_BTNFACE);
      back_brush = new CBrush(color);
      return (HBRUSH) (back_brush->m_hObject);
    }
  }
  return(CFormView::OnCtlColor(pDC, pWnd, nCtlColor));
}
76. 如何实现点击对话框外的地方使对话框到主窗口的后面
只能将桌面做为父窗口
pMDlg = new CMDlg;
pMDlg->Create(IDD_M_DIALOG,CWnd::GetDesktopWindow()/* 设置父窗口 */);
pMDlg->ShowWindow(SW_SHOW);
然后在任务栏里隐藏对话框程序
如何让对话框应用程序在在任务栏上不出现,并且不隐藏窗口。
[解决方法]
  把对话框的扩展属性修改成为WS_EX_TOOLWINDOW。
[程序实现]
  把对话框的属性设置成为toolwindow,然后在需要的地方执行本代码。
  DWORD Style = ::GetWindowLong(AfxGetMainWnd()->m_hWnd,GWL_EXSTYLE);
  Style = WS_EX_TOOLWINDOW ;
  AfxGetMainWnd()->ShowWindow(FALSE);
  ::SetWindowLong(AfxGetMainWnd()->m_hWnd,GWL_EXSTYLE,Style);
  AfxGetMainWnd()->ShowWindow(TRUE);

77. 想在程序一启动时就自动关闭窗口,不在任务栏里显示
用CTRL+W打开ClassWizard;
点击Class Info页,类名是工程名Dlg,
再在左下方的"Filter"中选择"Windows";
回到Message Maps页,就可以看到消息中有WM_WINDOWPOSCHANGING,
加入代码,如上所示.
这样运行*.EXE,不但看不到主界面,任务栏也没有,就是任务管理器中的"应用程序"中也不列出,那该如何关闭它?
在任务管理器的"进程"中可以找到它,这是黑客程序常用的方法.
如果需要的话,连"进程"中也看不到.这样要终止它就是问题了.
78.修改打印预览的ToolBar

为AFX_IDD_PREVIEW_TOOLBAR这个ID创建一个DialogBar。则系统就会用新创建的DialogBar代替系统默认的那个

79. 如何实现SDI与MDI的转换?

我想将一个编好的SDI应用程序转换为MDI,很明显要有多处的改变。
你可以这样做:建立一个继承于CMDIChidWnd的类,不防设为CChldFrm.在CWinApp中作如下变化。

InitInstance()
{
. ...
  //instead of adding CSingleDocTemplate
  // Add CMultiDocTemplate.
  pDocTemplate = new CMultiDocTemplate(
      IDR_MAINFRAME,
      RUNTIME_CLASS(CSDIDoc),
      RUNTIME_CLASS(CChldFrm),
// For Main MDI Frame change this frame window from
// CFrameWnd derivative ( i.e. CMainFrame )
// to your CMDIChildWnd derived CChldFrm.
      RUNTIME_CLASS(CSDIView));
/// After this it is required to create the main frame window
// which will contain all the child windows. Now this window is
// what was initially frame window for SDI.
  CMainFrame* pMainFrame = new CMainFrame;
  if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
      return FALSE;
   m_pMainWnd = pMainFrame;
.....
}
在从CMDIFrameWnd中继承的类CMainFrame代替CFramWnd后,所有的类都将从CMDIFrame继承,而不是CFrameWnd,编译运行后你就会发现程序已经从SDI变换到MDI。
注意:在CMainFram中必须将构造函数从private改为public.否则会出错。

80. CDC中的竖排文本?

在OnDraw成员函数中我想让文本竖直对齐,但CDC类似乎不支持该处理
方法一:如果你的竖直对齐是指旋转文本的话,下面的代码会对你有帮助:该代码检查一个Check box控制,查看文本是否需要旋转.
// m_pcfYTitle is a CFont* to the selected font.
// m_bTotateYTitle is a bool (==TRUE if rotated)
void CPage1::OnRotateytitle()
{
LOGFONT lgf;
m_pcfYTitle->GetLogFont(&lgf);
m_bRotateYTitle=
    ((CButton*)GetDlgItem(IDC_ROTATEYTITLE))->GetCheck()>0;
// escapement is reckoned clockwise in 1/10ths of a degree:
lgf.lfEscapement=-(m_bRotateYTitle*900);
m_pcfYTitle->DeleteObject();
m_pcfYTitle->CreateFontIndirect(&lgf);
DrawSampleChart();
}
注意如果你从CFontDialog中选择了不同的字体,你应该自己设定LOGFONT的lfEscapement成员.将初始化后的lfEscapement值传到CFontDialog中.
方法二:还有一段代码可参考:
LOGFONT LocalLogFont;
strcpy(LocalLogFont.lfFaceName, TypeFace);
LocalLogFont.lfWeight = fWeight;
LocalLogFont.lfEscapement = Orient;
LocalLogFont.lfOrientation = Orient;
if (MyFont.CreateFontIndirect(&LocalLogFont))
  {
  cMyOldFont = cdc->SelectObject(&MyFont);
  }

81. 如何用键盘滚动分割的视口?

我的问题是当我用鼠标滚动分割窗口时,视口滚动都很正常,但用键盘时,却什么也没有发生.
在你的视图继承类中加入如下两个函数,假定该类为CScrollerView:
void CScrollerView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    BOOL processed;
    for (unsigned int i=0;i< nRepCnt&&processed;i++)
        processed=KeyScroll(nChar);
    if (!processed)
      CScrollView::OnKeyDown(nChar, nRepCnt, nFlags);
}
BOOL CScrollerView::KeyScroll(UINT nChar)
{
    switch (nChar)
        {
        case VK_UP:
            OnVScroll(SB_LINEUP,0,NULL);
            break;
        case VK_DOWN:
            OnVScroll(SB_LINEDOWN,0,NULL);
            break;
        case VK_LEFT:
            OnHScroll(SB_LINELEFT,0,NULL);
            break;
        case VK_RIGHT:
            OnHScroll(SB_LINERIGHT,0,NULL);
            break;
        case VK_HOME:
            OnHScroll(SB_LEFT,0,NULL);
            break;
        case VK_END:
            OnHScroll(SB_RIGHT,0,NULL);
            break;
        case VK_PRIOR:
            OnVScroll(SB_PAGEUP,0,NULL);
            break;
        case VK_NEXT:
            OnVScroll(SB_PAGEDOWN,0,NULL);
            break;
        default:
            return FALSE; // not for us
               // and let the default class
               // process it.
        }
  return TRUE;
}

82. 如何改变默认的光标形状?

我试着将光标改变为其它的形状和颜色,但却没有变化.
在对话框/窗口/你需要的地方加上对WM_SETCURSOR消息的处理.
BOOL MyDialog::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
  // TOD Add your message handler code here and/or call default
  ::SetCursor(AfxGetApp()->LoadCursor(IDC_MYCURSOR));
  return TRUE;
  //return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
你没有成功的原因是因为窗口类光标风格不能为NULL.

83. 如何选择CTreeCtrl中的节点文本进行编辑?

在向CTreeCtrl中加入一项后,有什么方法可以编辑该节点的文本呢?
首先设置你的CcompTreeCtrl具有TVS_EDITLABELS属性.在设计时用控件属性来设置在运行时用GetStyle()/SetStyle()成员函数来设置.然后请看下述代码:
HTREEITEM CCompTreeCtrl::AddSet()
{
static int setCnt =3D 1;
HTREEITEM hItem;
CString csSet;
//create text for new note: New Set 1, New Set 2 ...
csSet.Format( _T( "New Set %d" ), setCnt++ );
hItem =3D InsertItem( csSet, IMG_CLOSEDFOLDER, IMG_CLOSEDFOLDER );
if( hItem !=3D NULL )
      EditLabel( hItem );
return hItem;
}

84. CListCtrl中选择变化时如何获得通知?

我在Report View中使用了一个CListCtrl(自绘制类型),我想知道什么时候选择项发生了改变.
在选择项变化时,可以使用按钮有效或失效,按如下操作:
 加入LVN_ITEMCHANGED消息处理.
void CYourClassNameHere::OnItemchangedEventList(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
*pResult = 0;
if (pNMListView->uChanged == LVIF_STATE)
{
 if (pNMListView->uNewState)
  GetDlgItem(IDC_DELETE)->EnableWindow(TRUE);
 else
  GetDlgItem(IDC_DELETE)->EnableWindow(FALSE);
}
}

85. List控件中整栏选择?

我在处理List控件时碰到了麻烦,我想创建一个ListView,来依据Tree控件的选择同时在ListView和ReportView中显示列表的信息.以下是相关的代码:
// Set full line select
ListView_SetExtendedListViewStyle(m_plstCustomers->GetSafeHwnd(),
LVS_EX_FULLROWSELECT);
按如下方法处理:
// -------------------- begin of snippet --------------------------------
bool CCommCtrlUtil32::ListCtrl_ModifyExtendedStyle(CListCtrl& p_rListCtrl,
                  const DWORD p_dwStyleEx,
                  const bool p_bAdd)
{
  HWND t_hWnd = p_rListCtrl.GetSafeHwnd();
  DWORD t_dwStyleEx = ListView_GetExtendedListViewStyle(t_hWnd);
  if(p_bAdd)
  {
    if(0 == (p_dwStyleEx & t_dwStyleEx))
    {
      // add style
      t_dwStyleEx |= p_dwStyleEx;
    }
  }
  else
  {
    if(0 != (p_dwStyleEx & t_dwStyleEx))
    {
      // remove style
      t_dwStyleEx &= ~p_dwStyleEx;
    }
  }
  ListView_SetExtendedListViewStyle(t_hWnd, t_dwStyleEx);
  return true;
}

86. 如何限制mdi子框架最大化时的大小?

用ptMaxTrackSize代替prMaxSize,如下所示:
void CChildFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
  // TOD Add your message handler code here and/or call default
  CChildFrame::OnGetMinMaxInfo(lpMMI);
  lpMMI->ptMaxTrackSize.x = 300;
  lpMMI->ptMaxTrackSize.y = 400;
}

87. 怎样实现3D效果?

在对话框中怎样实现Edit和Listboxes控件的3D效果?(环境95/NT VC5.0)
1). 使用带WS_EX_CLIENTEDGE标志的::CreateWindowEx来替换::CreateWindow 或者用CWnd::CreateEx替换CWnd::Create.
2).在建立控件之后,调用ModifyStyleEx(0, WS_EX_CLIENTEDGE).

88. How do I update the text of a pane in a status bar?

By default, a CStatusBar pane is not enabled when the pane is created. To activate a pane, you must call the ON_UPDATE_COMMAND_UI() macro for each pane on the status bar and update the panes. Because panes do not send WM_COMMAND messages, you cannot use ClassWizard to activate panes; you must type the code manually. For example, suppose one pane has ID_INDICATOR_PAGE as its identifier and that it contains the current page number in a document. To make the ID_INDICATOR_PAGE pane display text, add the following to a header file (probably the MAINFRM.H file):

afx_msg void OnUpdatePage(CCmdUI *pCmdUI);

Add the following to the application message map:

ON_UPDATE_COMMAND_UI(ID_INDICATOR_PAGE, OnUpdatePage)

Add the following to a source code file (probably MAINFRM.CPP):

void CMainFrame::OnUpdatePage(CCmdUI *pCmdUI)
{
  pCmdUI->Enable();
}

To display text in the panes, either call SetPaneText() or call CCmdUI::SetText() in the OnUpdate() function. For example, you might want to set up an integer variable m_nPage that contains the current page number. Then, the OnUpdatePage() function might read as follows:

void CMainFrame::OnUpdatePage(CCmdUI *pCmdUI)
{
  pCmdUI->Enable();
  char szPage[16];
  wsprintf((LPSTR)szPage, "Page %d", m_nPage);
  pCmdUI->SetText((LPSTR)szPage);
}

This technique causes the page number to appear in the pane during idle processing in the same manner that the application updates other indicators.

89. 动态修改对话框的大小

 [问题提出]
  关于如何动态改变对话框的大小,我做了个Demo,大家看看.

 [程序实现]
   //本函数使用方法:
   //第一个参数:如果是TRUE表示显示扩展的对话框,如果是FALSE,表示缩小对话框。
   //第二个参数:表示本对话框的HWND,
   //第三个参数:表示缩小后大小的控件的ID
  void COptionDlg::ExpandBox(BOOL fExpand, HWND hwnd, int nIDDefaultBox)
  {
     CWnd *pWndBox=GetDlgItem(nIDDefaultBox);
     RECT rcDefaultBox,rcChild,rcIntersection,rcWnd;
     pWndBox->GetWindowRect(&rcDefaultBox);
     HWND hwndChild = ::GetTopWindow(hwnd);
     for (; hwndChild != NULL; hwndChild = ::GetNextWindow(hwndChild,GW_HWNDNEXT))
     {
         ::GetWindowRect(hwndChild, &rcChild);
         if (!IntersectRect(&rcIntersection, &rcChild, &rcDefaultBox))
              ::EnableWindow(hwndChild, fExpand);
     }
     ::GetWindowRect(hwnd, &rcWnd);
     if (GetWindowLong(hwnd, GWL_USERDATA) == 0)
     {
         SetWindowLong(hwnd, GWL_USERDATA,
              MAKELONG(rcWnd.right - rcWnd.left,
              rcWnd.bottom - rcWnd.top));
         ::ShowWindow(pWndBox->m_hWnd, SW_HIDE);
     }
     ::SetWindowPos(hwnd, NULL, 0, 0,
         rcDefaultBox.right - rcWnd.left,
         rcDefaultBox.bottom - rcWnd.top,
         SWP_NOZORDER | SWP_NOMOVE);
     if(fExpand)
     {
         DWORD dwDims = GetWindowLong(hwnd, GWL_USERDATA);
         ::SetWindowPos(hwnd, NULL, 0, 0,
              LOWORD(dwDims), HIWORD(dwDims), SWP_NOZORDER | SWP_NOMOVE);
         ::SendMessage(hwnd, DM_REPOSITION, 0, 0);
     }
  }

90. 用DoModal()调用模态对话框,总是显示在正中,我重载了它,并添加了MoveWindow(),可是其m_hWnd是一串零,调用失败。请问有何方法可使调用的模态对话框显示于自定义位置?多谢
  我不清楚你把MoveWindow()加在什么地方了,正确的方法是在OnInitDialog中添加MoveWindow,如:
   MoveWindow(0, 1, 300, 200);
  需要注意的是前两个参数不能都为0。如果你确实希望把窗口放在(0, 0)处,可以在对话框设计窗口的属性中选中Absolute Align,然后再加入
   MoveWindow(0, 0, 300, 200);
  为什么会是这样?你看了MFC的源程序就会明白。原来MFC在调用你的OnInitDialog之后,会调用CDialog::CheckAutoCenter()(在dlgcore.cpp中)检查是否需要将窗口居中,你看了这个函数后就明白为什么需要上面那么做了。


你可能感兴趣的:([转]MFC学习总结(五))