现在的位置: 首页 > 综合 > 正文

MFC常用的一些方法

2013年12月03日 ⁄ 综合 ⁄ 共 7801字 ⁄ 字号 评论关闭

发布程序的时候我们应该选择Release版本
这样就不会因为缺少mfc42d.dll mfco42d.dll msvcrtd.dll mfcn42d.dll
而出现问题了

一 对话框 ----- CDialog ...
1:如何改变CFileDialog的标题
::
opendlg.m_ofn.lpstrTitle = "增加文件";

2:对话框打开时多选/复选
::
OFN_ALLOWMULTISELECT属性,然后
opendlg.m_ofn.nMaxFile = 4096;
char* szbuffer = new char[4096];
opendlg.m_ofn.lpstrFile = szbuffer;
opendlg.m_ofn.lpstrFile[0] = NULL;
POSITION    pos    =    fd.GetStartPosition();   
while    (pos    !=    NULL)   
{   
CString    strFilePath=fd.GetNextPathName(pos);   
}
delete [] szbuffer;

二 控件之ListBox
1:List Box Horizontal/Vertical Extent显示
::
需要在对话框初始化中加入m_ListBox->SetHorizontalExtent(500);

三 控件操作
1:改变控件颜色(如按钮)
::
HBRUSH CMyDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
if(nCtlColor == CTLCOLOR_BTN )
{
   pDC->SetBkColor(RGB(255,0,0));
   pDC->SetTextColor(RGB(0,255,0));
   return m_hBrush;
}
// TODO: Return a different brush if the default is not desired
return hbr;
}
在OnInitDialog里面添加m_hBrush = CreateSolidBrush(RGB(255,0,0));就可以了改变你的按钮的颜色了,m_hBrush为全局HBRUSH变量。但会因此失去上面的文字,最好还是自绘控件。
2 自绘按钮
当有了自己的CMyButton之后,只需要添加:DDX_Control(pDX, IDC_BT_FS, m_dxButton);
其中IDC_BT_FS是从ms控件中添加的按钮,而m_dxButton则是自己的按钮类型变量。

3 DragAcceptFiles
需要在OnInitialUpdate 中调用DragAcceptFiles(TRUE);
四 其他接口

1 DirectShow 判断是否有视频
【DSHOW中判断媒体文件是否有视频】   
   BOOL    CheckIsVisiable(LPCTSTR    szFile)   
   {   
     CComPtr<    IMediaDet    >    pDet;   
     BOOL    FoundVideo    =    FALSE;   
    
     pDet.CoCreateInstance(CLSID_MediaDet);     
     if(!pDet)   
     {   
       AfxMessageBox(TEXT("Could    not    create    CLSID_MediaDet"));   
     }   
     else   
     {   
       USES_CONVERSION;   
       HRESULT    hr;   
    
       //    For    UNICODE    support,    copy    the    CString    into    a    TCHAR    array   
       TCHAR    szFilename[MAX_PATH];   
       _tcscpy(szFilename,    (LPCTSTR)    szFile);   
    
       hr    =    pDet->put_Filename(T2W(szFilename));   
       if    (FAILED(hr))   
       {   
         AfxMessageBox(TEXT("Failed    to    put    filename"));   
         return    -1;   
       }   
       long    StreamCount    =    0;   
       hr    =    pDet->get_OutputStreams(&StreamCount);   
       if    (FAILED(hr))   
       {   
         AfxMessageBox(TEXT("Failed    to    get    output    streams"));   
         return    -1;   
       }   
    
       for(int    i    =    0;    i    <    StreamCount;    i++)   
       {   
         hr    =    pDet->put_CurrentStream(i);   
         if    (FAILED(hr))   
         {   
           AfxMessageBox(TEXT("Failed    to    put    current    stream"));   
           return    -1;   
         }   
         GUID    StreamType;   
         hr    =    pDet->get_StreamType(&StreamType);   
         if    (FAILED(hr))   
         {   
           AfxMessageBox(TEXT("Failed    to    get    stream    type"));   
           return    -1;   
         }   
         if(StreamType    ==    MEDIATYPE_Video)   
         {   
           FoundVideo    =    TRUE;   
         }   
       }   
     }   
     return    FoundVideo;   
   }  

五 WINAPI
1 得到屏幕分辨率
nWidth = GetSystemMetrics(SM_CXSCREEN);
nHeight = GetSystemMetrics(SM_CYSCREEN);
2 得到应用程序所在路径
char m_Directory = new char[100];
GetModuleFileName(AfxGetInstanceHandle(), m_Directory,100);
delete [] m_Directory
得到结果包含了应用程序名称,如C:/xxx.exe
3 ShowWindow
ShowWindow的API函数是显示窗口,但它在第一次调用和以后的调用是有区别的。第一次调用时,它的输入参数nCmdShow是需要输入WinMain函数里传入来的nCmdShow参数,而不能是其它参数。

函数声明如下:
WINUSERAPI
BOOL
WINAPI
ShowWindow(
     __in HWND hWnd,
     __in int nCmdShow);
hWnd是窗口的句柄。
nCmdShow是窗口显示的状态。可能设置的值如下:
SW_FORCEMINIMIZE是强制窗口最小化,主要使用在非窗口主线程的其它线程来操作。
SW_HIDE是显示窗口为隐藏状态。
SW_MAXIMIZE是显示窗口为最大化。
SW_MINIMIZE是显示窗口为最小化。
SW_RESTORE是从任务里恢复窗口显示。
SW_SHOW是激活窗口为当前窗口,并且显示为当前的大小和位置。
SW_SHOWDEFAULT是创建进程时显示窗口的值。
SW_SHOWMAXIMIZED是激活窗口为当前窗口,并且显示最大化。
SW_SHOWMINIMIZED是激活窗口为当前窗口,并且显示最小化。
SW_SHOWMINNOACTIVE是显示窗口为最小化,但不激活它作为当前窗口。
SW_SHOWNA是显示为当前的大小和位置,但不激活它作为当前窗口。
SW_SHOWNOACTIVATE是显示当前窗口,但不激活它作为当前窗口。
SW_SHOWNORMAL是显示当前窗口,但窗口是最小化或最大化时会恢复窗口为原来的大小和位置。

调用这个函数的实例如下:
#001 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
#002 {
#003   HWND hWnd;
#004
#005   hInst = hInstance; // 保存实例句柄到全局变量里。
#006
#007   hWnd = CreateWindow(szWindowClass,
#008          szTitle,
#009          WS_OVERLAPPEDWINDOW,
#010          CW_USEDEFAULT,
#011          0,
#012          CW_USEDEFAULT,
#013          0,
#014          NULL,
#015          NULL,
#016          hInstance,
#017          NULL);
#018
#019   if (!hWnd)
#020   {
#021          return FALSE;
#022   }
#023
#024   ShowWindow(hWnd, nCmdShow);
#025   UpdateWindow(hWnd);
#026
#027   //
#028   //蔡军生 2007/07/14
#029   //显示窗口测试。
#030   MessageBox(NULL,_T("最大化"),_T("测试"),MB_OK);
#031
#032   //显示窗口为最大化。
#033   ShowWindow(hWnd, SW_SHOWMAXIMIZED);
#034
#035  
#036   MessageBox(NULL,_T("原来位置"),_T("测试"),MB_OK);
#037
#038   //显示窗口为原来位置。
#039   ShowWindow(hWnd, SW_SHOWNORMAL);
#040
#041
#042   return TRUE;
#043 }

在第24行里先调用这个函数显示创建进程的窗口状态。
第33行里显示窗口为最大化。
第39行里显示窗口为原来的状态。

这样就可以掌握了ShowWindow函数的使用。

4 双缓冲技术绘图
显示图形如何避免闪烁,如何提高显示效率是问得比较多的问题。
而且多数人认为MFC的绘图函数效率很低,总是想寻求其它的解决方案。
MFC的绘图效率的确不高但也不差,而且它的绘图函数使用非常简单,
只要使用方法得当,再加上一些技巧,用MFC可以得到效率很高的绘图程序。
我想就我长期(呵呵当然也只有2年多)使用MFC绘图的经验谈谈
我的一些观点。

1、显示的图形为什么会闪烁?
     我们的绘图过程大多放在OnDraw或者OnPaint函数中,OnDraw在进行屏
幕显示时是由OnPaint进行调用的。当窗口由于任何原因需要重绘时,
总是先用背景色将显示区清除,然后才调用OnPaint,而背景色往往与绘图内容
反差很大,这样在短时间内背景色与显示图形的交替出现,使得显示窗口看起来
在闪。如果将背景刷设置成NULL,这样无论怎样重绘图形都不会闪了。
当然,这样做会使得窗口的显示乱成一团,因为重绘时没有背景色对原来
绘制的图形进行清除,而又叠加上了新的图形。
     有的人会说,闪烁是因为绘图的速度太慢或者显示的图形太复杂造成的,
其实这样说并不对,绘图的显示速度对闪烁的影响不是根本性的。
例如在OnDraw(CDC *pDC)中这样写:
pDC->MoveTo(0,0);
pDC->LineTo(100,100);
这个绘图过程应该是非常简单、非常快了吧,但是拉动窗口变化时还是会看见
闪烁。其实从道理上讲,画图的过程越复杂越慢闪烁应该越少,因为绘图用的
时间与用背景清除屏幕所花的时间的比例越大人对闪烁的感觉会越不明显。
比如:清楚屏幕时间为1s绘图时间也是为1s,这样在10s内的连续重画中就要闪
烁5次;如果清楚屏幕时间为1s不变,而绘图时间为9s,这样10s内的连续重画
只会闪烁一次。这个也可以试验,在OnDraw(CDC *pDC)中这样写:
for(int i=0;i<100000;i++)
{
   pDC->MoveTo(0,i);
   pDC->LineTo(1000,i);
}
呵呵,程序有点变态,但是能说明问题。
     说到这里可能又有人要说了,为什么一个简单图形看起来没有复杂图形那么
闪呢?这是因为复杂图形占的面积大,重画时造成的反差比较大,所以感觉上要
闪得厉害一些,但是闪烁频率要低。
     那为什么动画的重画频率高,而看起来却不闪?这里,我就要再次强调了,
闪烁是什么?闪烁就是反差,反差越大,闪烁越厉害。因为动画的连续两个帧之间
的差异很小所以看起来不闪。如果不信,可以在动画的每一帧中间加一张纯白的帧,
不闪才怪呢。

2、如何避免闪烁
     在知道图形显示闪烁的原因之后,对症下药就好办了。首先当然是去掉MFC
提供的背景绘制过程了。实现的方法很多,
   * 可以在窗口形成时给窗口的注册类的背景刷付NULL
   * 也可以在形成以后修改背景
static CBrush brush(RGB(255,0,0));
SetClassLong(this->m_hWnd,GCL_HBRBACKGROUND,(LONG)(HBRUSH)brush);
   * 要简单也可以重载OnEraseBkgnd(CDC* pDC)直接返回TRUE
     这样背景没有了,结果图形显示的确不闪了,但是显示也象前面所说的一样,
变得一团乱。怎么办?这就要用到双缓存的方法了。双缓冲就是除了在屏幕上有
图形进行显示以外,在内存中也有图形在绘制。我们可以把要显示的图形先在内存中
绘制好,然后再一次性的将内存中的图形按照一个点一个点地覆盖到屏幕上去(这个
过程非常快,因为是非常规整的内存拷贝)。这样在内存中绘图时,随便用什么反差
大的背景色进行清除都不会闪,因为看不见。当贴到屏幕上时,因为内存中最终的图形
与屏幕显示图形差别很小(如果没有运动,当然就没有差别),这样看起来就不会闪。

3、如何实现双缓冲
     首先给出实现的程序,然后再解释,同样是在OnDraw(CDC *pDC)中:

CDC MemDC; //首先定义一个显示设备对象
CBitmap MemBitmap;//定义一个位图对象

//随后建立与屏幕显示兼容的内存显示设备
MemDC.CreateCompatibleDC(NULL);
//这时还不能绘图,因为没有地方画 ^_^
//下面建立一个与屏幕显示兼容的位图,至于位图的大小嘛,可以用窗口的大小
MemBitmap.CreateCompatibleBitmap(pDC,nWidth,nHeight);
  
//将位图选入到内存显示设备中
//只有选入了位图的内存显示设备才有地方绘图,画到指定的位图上
CBitmap *pOldBit=MemDC.SelectObject(&MemBitmap);

//先用背景色将位图清除干净,这里我用的是白色作为背景
//你也可以用自己应该用的颜色
MemDC.FillSolidRect(0,0,nWidth,nHeight,RGB(255,255,255));

//绘图
MemDC.MoveTo(……);
MemDC.LineTo(……);

//将内存中的图拷贝到屏幕上进行显示
pDC->BitBlt(0,0,nWidth,nHeight,&MemDC,0,0,SRCCOPY);

//绘图完成后的清理
MemBitmap.DeleteObject();
MemDC.DeleteDC();

上面的注释应该很详尽了,废话就不多说了。

4、如何提高绘图的效率
     我主要做的是电力系统的网络图形的CAD软件,在一个窗口中往往要显示成千上万个电力元件,而每个元件又是由点、线、圆等基本图形构成。如果真要在一次重绘过程重画这么多元件,可想而知这个过程是非常漫长的。如果加上了图形的浏览功能,鼠标拖动图形滚动时需要进行大量的重绘,速度会慢得让用户将无法忍受。怎么办?只有再研究研究MFC的绘图过程了。
     实际上,在OnDraw(CDC *pDC)中绘制的图并不是所有都显示了的,例如:你
在OnDraw中画了两个矩形,在一次重绘中虽然两个矩形的绘制函数都有执行,但是很有可能只有一个显示了,这是因为MFC本身为了提高重绘的效率设置了裁剪区。裁剪区的作用就是:只有在这个区内的绘图过程才会真正有效,在区外的是无效的,即使在区外执行了绘图函数也是不会显示的。因为多数情况下窗口重绘的产生大多是因为窗口部分被遮挡或者窗口有滚动发生,改变的区域并不是整个图形而只有一小部分,这一部分需要改变的就是pDC中的裁剪区了。因为显示(往内存或者显存都叫显示)比绘图过程的计算要费时得多,有了裁剪区后显示的就只是应该显示的部分,大大提高了显示效率。但是这个裁剪区是MFC设置的,它已经为我们提高了显示效率,在进行复杂图形的绘制时如何进一步提高效率呢?那就只有去掉在裁剪区外的绘图过程了。可以先用pDC->GetCliPBox()得到裁剪区,然后在绘图时判断你的图形是否在这个区内,如果在就画,不在就不画。
如果你的绘图过程不复杂,这样做可能对你的绘图效率不会有提高。

5 多个View处理
CMainFrame* pWnd = (CMainFrame*)AfxGetMainWnd();
CTView* pView = (CTView*)pWnd->GetActiveView();
CDocument *p = pView -> GetDocument() ;
POSITION    pos    =    p->GetFirstViewPosition();    
CTreeView*    pVw;    
   while(pos    !=    NULL)    
   {    
   pVw=(CTView*)p->GetNextView(pos);    
   if(pVw->IsKindOf(RUNTIME_CLASS(CTView)))    
   break;   
}
6 如何解决SDI/MDI视口闪烁问题,比如在其上加Directshow
在view的OnEraseBkgnd中,返回true阻止重绘背景。

抱歉!评论已关闭.