VC常见的108个问题(41-80)
2015-01-23 14:15
405 查看
(41) 在不使用通用文件打开对话的情况下如何显示一个文件列表
调用CWnd::DlgDirList或者CWnd::DlgDirListComboBox,Windows 将自动地向列表框或组合框填充可用的驱动器名或者指定目录中的文件,下例将Windows目录中的文件填充在组合框中:
BOOL CSampleDig::OnInitDialog()
{
CDialog::OnInitDialog();
TCHAR szPath [MAX_PATH] = {"c:\\windows"};
int nReslt = DlgDirListComboBox(szPath, IDC_COMBO, IDC_CURIDIR, DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_ARCHIVE);
return TRUE;
}
(42) 为什么旋转按钮控件看起来倒转
需要调用CSpinCtrl::SetRange 设置旋转按钮控件的范围,旋转按钮控件的缺省上限为0,缺省下限为100,这意味着增加时旋转按控件的值由100变为0。下例将旋转按钮控件的范围设置为0到100:
BOOL CAboutDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//set the lower and upper limit of the spin button
m_wndSpin.SetRange( 0 ,100 );
return TRUE;
}
Visual C++ 4.0 Print对话中的Copise旋转按钮控件也有同样的问题:按下Up按钮时拷贝的数目减少,而按下Down 按钮时拷贝的数目增加。
(43) 为什么旋转按钮控件不能自动地更新它下面的编辑控件
如果使用旋转按钮的autu buddy特性, 则必须保证在对话的标记顺序中buddy窗口优先于旋转按钮控件。从Layout菜单中选择Tab Order菜单项(或者按下Crtl+D)可以设置对话的标签顺序。
(44) 如何用位图显示下压按钮
Windows 95按钮有几处新的创建风格,尤其是BS_BITMAP和BS_ICON,要想具有位图按钮,创建按钮和调用CButton::SetBitmap或CButton::SetIcon时要指定BS_BITMAP或BS_ICON风格。
首先,设置按钮的图标属性。然后,当对话初始化时调用CButton::SetIcon。注意:下例用图标代替位图,使用位图时要小心,因为不知道背景所有的颜色——并非每个人都使用浅灰色。
BOOL CSampleDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//set the images for the push buttons .
BOOL CSampleDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//set the images for the push buttons .
m_wndButton1.SetIcon(AfxGetApp()->LoadIcon(IDI_IPTION1));
m_wndButton2.SetIcon(AfxGetApp()->LoadIcon(IDI_IPTION2));
m_wndButton3.SetIcon(AfxGetApp()->LoadIcon(IDI_IPTION3));
return TRUE;
}
(45) 如何一个创建三态下压按钮
可以使用新的BS_PUSHBUTTON 风格位和检测框以及按钮来创建一个三态下压按钮。这很容易,只需将检测框和按钮拖拉到对话中并指定属性Push—like即可。不用任何附加程序就可以成为三态下压按钮。
(46) 如何动态创建控件
分配一个控件对象的实例并调用其Create成员函数。开发者最容易忽略两件事:忘记指定WS_VISBLE标签和在栈中分配控件对象。下例动态地创建一个下压按钮控件:
//In class declaration(.H file ).
private : CButton* m _pButton;
//In class implementation(.cpp file ) .
m_pButton =new CButton;
ASSERT_VALID(m_pButton);
m_pButton->Create(_T("Button Title ") , WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON. Crect( 0, 0, 100 , 24) , this , IDC_MYBUTTON );
(47) 如何限制编辑框中的准许字符
如果用户在编辑控件中只允许接收数字,可以使用一个标准的编辑控件并指定新的创建标志ES_NUMBERS,它是Windows 95新增加的标志,该标志限制 编辑控件只按收数字字符。如果用户需要复杂的编辑控件,可以使用Microsoft 的屏蔽编辑控件,它是一个很有用的OLE定制控件。
如果希望不使用OLE 定制控件自己处理字符,可以派生一个CEdit类并处理WM_CHAR消息,然后从编辑控件中过滤出特定的字符。首先,使用ClassWizard建立一个 CEdit的派生类,其次,在对话类中指定一个成员变量将编辑控件分类在OnInitdialog 中调用CWnd::SubclassDlgItem .
//In your dialog class declaration(.H file )
private : CMyEdit m_wndEdit // Instance of your new edit control .
//In you dialog class implementation(.CPP file )
BOOL CSampleDialog::OnInitDialog()
{
…
//Subclass the edit lontrod .
m_wndEdit.SubclassDlgItem(IDC_EDIT,this);
…
}
使用ClassWizard处理WM_CHAR消息,计算nChar参量并决定所执行的操作,用户可以确定是否修改、传送字符。下例说明了如何显示字母字符,如果字符是字母字符,则调用CWnd OnChar,否则不调用OnChar.
//Only display alphabetic dharacters .
void CMyEdit::OnChar(UINT nChar , UINT nRepCnt , UITN nFlags )
{
//Determine if nChar is an alphabetic character.
if(::IsCharAlpha((TCHAR) nChar ) )
CEdit::OnChar(nChar, nRepCnt , nFlags);
}
如果要修改字符,则不能仅仅简单地用修改过的nChar调用CEdit::OnChar,然后CEdit::OnChar调用 CWnd::Default获取原来的wParam 和lParam 的值,这样是不行的。要修改一个字符,需要首先修改nChar,然后用修改过的nChar调用CWnd::DefWindowProc。下例说明了如何将字符转变为大写:
//Make all characters uppercase
void CMyEdit::OnChar(UINT nChar , UINT nRepCnt , UINT nFlags )
{
//Make sure character is uppercase .
if(::IsCharAlpha((TCHAR) nChar)
nChar=::CharUpper(nChar);
//Bypass default OnChar processing and directly call default window proc.
DefWindProc(WM_CHAR, nChar , MAKELPARAM(nRepCnt, nFlags));
}
(48) 如何改变控件的颜色
有两种方法。其一,可以在父类中指定控件的颜色,或者利用MFC4.0新的消息反射在控件类中指定颜色。 当控件需要重新着色时,工作框调用父窗口(通常是对话框)的CWnd::OnCrtlColor,可以在父窗口类中重置该函数并指定控件的新的绘画属性。例如,下述代码将对话中的所有编辑控件文本颜色改为红色:
HBRUSH CAboutDig::OnCtlColor(CDC * pDCM , CWnd * pWnd , UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd , nCtlColor);
//Draw red text for all edit controls .
if(nCtlColor == CTLCOLOR_EDIT)
pDC->SetTextColor(RGB(255, 0, 0));
return hbr;
}
然而,由于每个父窗口必须处理通知消息并指定每个控件的绘画属性,所以,这种方法不是完全的面向对象的方法。控件处理该消息并指定绘画属性更合情合理。消息反射允许用户这样做。通知消息首先发送给父窗口,如果父窗口没有处理则发送给控件。创建一个定制彩色列表框控件必须遵循下述步骤。
首先,使用ClassWizard 创建一个CListBox 的派生类并为该类添加下述数据成员。
class CMyListBox publilc CListBox
{
…
private
COLORREF m_clrFor; // foreground color
COLORREF m_clrBack; //background color
Cbrush m_brush; //background brush
…
}
其次,在类的构造函数中,初始化数据中。
CMyListBox::CMyListBox()
{
//Initialize data members .
m_clrFore = RGB(255 , 255 , 0); //yellow text
m_clrBack = RGB(0 , 0 , 255); // blue background
m_brush.CreateSolidBrush(m_clrBack);
}
最后,使用ClassWizard处理反射的WM_CTLCOLOR(=WM_CTLCOLOR)消息并指定新的绘画属性。
HBRUSH CMyListBox::CtlColor(CDC* pDC, UINT nCtlColor)
{
pDC->SetTextColor(m_clrFore);
pDC->SetBkColor(m_clrBack);
return(HBRUSH) m_brush.GetSafeHandle();
}
现在,控件可以自己决定如何绘画,与父窗口无关。
(49) 当向列表框中添加多个项时如何防止闪烁
调用CWnd::SetRedraw 清除重画标志可以禁止CListBox(或者窗口)重画。当向列表框添加几个项时,用户可以清除重画标志,然后添加项,最后恢复重画标志。为确保重画列表框的新项,调用SetRedraw(TRUE) 之后调用CWnd::Invalidate。
//Disable redrawing.
pListBox->SetRedraw(FALSE);
//Fill in the list box gere
//Enable drwing and make sure list box is redrawn.
pListBox->SetRedraw(TRUE);
pListBox->Invalidate();
(50) 如何向编辑控件中添加文本
由于没有CEdit::AppendText函数,用户只好自己做此项工作。调用CEdit::SetSel移动到编辑控件末尾,然后调用CEdit::ReplaceSel添加文本。下例是AppendText 的一种实现方法:
void CMyEdit::AppendText(LPCSTR pText)
{
int nLen=GetWindowTextLength();
SetFocus();
SetSel(nLen, nLen);
ReplaceSel(pText);
}
(51) 如何访问预定义的GDI对象
可以通过调用CDC::SlectStockObject使用Windows的几个预定义的对象,诸如刷子、笔以及字体。下例使用了Windows预定义的笔和刷子GDI对象在视窗中画一个椭圆。
//Draw ellipse using stock black pen and gray brush.
void CSampleView::OnDraw(CDC* pDC)
{
//Determine size of view.
CRect rcView;
GetClientRect(rcView);
//Use stock black pen and stock gray brush to draw ellipse.
pDC->SelectStockObject(BLACK_PEN);
pDC->SelectStockObject(GRAY_BRUSH);
//Draw the ellipse.
pDC->Ellipse(reView);
}
也可以调用新的SDK函数GetSysColorBrush获取一个系统颜色刷子,下例用背景色在视窗中画一个椭圆:
void CsampleView::OnDraw(CDC* pDC)
{
//Determine size of view.
CRect rcView;
GetClientRect(rcView);
//Use background color for tooltips brush.
CBrush * pOrgBrush=pDC->SelectObject( CBrush::FromHandle(::GetSysColorBrush(COLOR_INFOBK)));
//Draw the ellipse.
pDC->Ellipse(rcView);
//Restore original brush.
pDC->SelectObject(pOrgBrush);
}
(52) 如何获取GDI对象的属性信息
可以调用GDIObject::GetObject。这个函数将指定图表设备的消息写入到缓冲区。下例创建了几个有用的辅助函数。
//Determine if font is bold.
BOOL IsFontBold(const CFont&font)
{
LOGFONT stFont;
font.GetObject(sizeof(LOGFONT), &stFont);
return(stFont.lfBold)? TRUE: FALSE;
}
//Return the size of a bitmap.
CSize GetBitmapSize(const CBitmap&bitmap)
{
BITMAP stBitmap;
bitmap.GetObject(sizeof(BITMAP), &stBitmap);
return CSize(stBitmap.bmWidth, stBitmap.bmHeight);
}
//Create a pen with the same color as a brush.
BOOL CreatePenFromBrush(Cpen&pen, cost Cbrush&brush)
{
LOGBRUSH stBrush;
brush.Getobject(sizeof(LOGBRUSH), &stBrush);
return pen. Createpen(PS_SOLID, 0, stBrush.ibColor);
}
(53) 如何实现一个橡皮区矩形
CRectTracker是一个很有用的类,可以通过调用CRectTracker::TrackRubberBand 响应WM_LBUTTONDOWN消息来创建一个橡皮区矩形。
下例表明使用CRectTracker移动和重置视窗中的蓝色椭圆的大小是很容易的事情。
首先,在文件档中声明一个CRectTracker数据成员:
class CSampleView : Public CView
{
…
public :
CrectTracker m_tracker;
…
}
其次,在文档类的构造函数中初始化CRectTracker 对象:
CSampleDoc::CSampleDOC()
{
//Initialize tracker position, size and style.
m_tracker.m_rect.SetRect(0, 0, 10, 10);
m_tracker.m_nStyle=CRectTracker::resizeInside | CRectTracker::dottedLine;
}
然后,在OnDraw函数中画椭圆和踪迹矩形:
void CSampleView::OnDraw(CDC* pDC)
{
CSampleDoc* pDoc=GetDocument();
ASSERT_VALID(pDoc);
//Select blue brush into device context.
CBrush brush(RGB(0, 0, 255));
CBrush* pOldBrush=pDC->SelectObject(&brush);
//draw ellipse in tracking rectangle.
Crect rcEllipse;
pDoc->m_tracker.GetTrueRect(rcEllipse);
pDC->Ellipse(rcEllipse);
//Draw tracking rectangle.
pDoc->m_tracker.Draw(pDC);
//Select blue brush out of device context.
pDC->Selectobject(pOldBrush);
}
最后,使用ClassWizard处理WM_LBUTTONDOWN消息,并增加下述代码。该段代码根据鼠标击键情况可以拖放、移动或者重置椭圆的大小。
void CSampleView::OnLButtonDown(UINT nFlags, CPoint point)
{
//Get pointer to document.
CSampleDoc* pDoc=GetDocument();
ASSERT_VALID(pDoc);
//If clicked on ellipse, drag or resize it.Otherwise create a
//rubber-band rectangle nd create a new ellipse.
BOOL bResult=pDoc->m_tracker.HitTest(point)!= CRectTracker::hitNothing
//Tracker rectangle changed so update views.
if(bResult)
{
pDoc->m_tracker.Track(this,point,TRue);
pDoc->SetModifiedFlag();
pDoc->UpdateAllViews(NULL);
}
else
pDoc->m-tracker.TrackRubberBand(this,point,TRUE);
CView::onLButtonDown(nFlags,point);
}
(54) 如何更新翻转背景颜色的文本
调用CDC::SetBkmode并传送OPAQUE用当前的背景颜色填充背景,或者调用CDC::SetBkMode并传送TRANSPAARENT使背景保持不变,这两种方法都可以设置背景模式。下例设置背景模式为TRANSPARENT,可以两次更新串,用花色带黑阴影更新文本。黑色串在红色串之后,但由于设置了背景模式仍然可见。
void CSampleView::OnDraw(CDC* pDC)
{
//Determint size of view.
CRect rcView;
GetClientRect(rcVieew);
//Create sample string to display.
CString str(_T("Awesome Shadow Text..."));
//Set the background mode to transparent.
pDC->SetBKMode(TRANSPARENT);
//Draw black shadow text.
rcView.OffsetRect(1, 1);
pDc->SetTextColor(RGB(0, 0, 0));
pDC->DrawText(str, str.GetLength(), rcView, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
//Draw red text.
rcView.OffsetRect(-1,-1);
pDc->SetTextColor(RGB(255, 0, 0));
pDC->DrawText(str, str.GetLength(), rcView, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
(55) 如何创建一个具有特定点大小的字体
可以指定字体逻辑单位的大小,但有时指定字体的点的大小可能会更方便一些。可以如下将字体的点转换为字体的高度:
int nHeigth=mulDiv(nPointSize, dc.GetDeviceCaps(LOGPIXELSY), 72);
下例创建了一个8点的Apial字体:
…
CClientDC dc(AqfxGetMainWnd());
m_font.CreateFont(MulDiv(8, dc.GetDeviceCaps(LOGPIXELSY), 72), 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET, OUT_STROKE_PRECIS, CLIP_STROKE_PRECIS, DRAFT_QUALITY, VARIABLE_PITCH | FF-SWISS,_T("Arial"));
(56) 如何计算一个串的大小
函数CDC::GetTextExtent 根据当前选择的字体计算一个串的高度和宽度。如果使用的不是系统字体而是其他字体,则在调用GetTextExtent之前将字体选进设备上下文中是很重要的,否则计算高度和宽度时将依据系统字体,由此得出的结果当然是不正确的。下述样板程序当改变下压按钮的标题时动态调整按钮的大小,按钮的大小由按钮的字体和标题的大小而定。响应消息WM_SETTEXT时调用OnSetText,该消息使用ON_MESSAE宏指令定义的用户自定义消息。
LRESULT CMyButton::OnSettext(WPARAM wParam, LPARAM lParam)
{
//Pass message to window procedure.
LRESULT bResult=CallWindowProc(*GetSuperWndProcAddr(), m_hWnd, GetCurrentMessage()->message,wParam,lParam);
//Get title of push button.
CString strTitle;
GetWindowText(strTitle);
//Select current font into device context.
CDC* pDC=GetDc();
CFont* pFont = GetFont();
CFont* pOldFont = pDC->SelectObject(pFont);
//Calculate size of title.
CSize size=pDC->GetTextExent(strTitle,strTitle.GetLength());
//Adjust the button's size based on its title.
//Add a 5-pixel border around the button.
SetWindowPos(NULL, 0, 0, size.cx+10, size.cy+10, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
//Clean up.
pDC->SelectFont(pOldFont);
ReleaseDC(pDC);
return bResult;
}
(57) 如何显示旋转文本
只要用户使用TrueType或者GDI笔或字体就可以显示旋转文本(有些硬件设备也支持旋转光栅字体)。LOGFONT结构中的 ifEscapement成员指定了文本行和x轴的角度,角度的单位是十分之一度而不是度,例如,ifEscapement为450表示字体旋转45度。为确保所有的字体沿坐标系统的同一方向旋转,一定要设置ifEscapement成员的CLIP_LH_ANGLES位,否则,有些字体可能反向旋转。下例使用了14点Arial字体每间隔15度画一个串。
void CSampleView::OnDraw(CDC* pDC)
{
//Determine the size of the window.
CRect rcClient;
GetClientRect(rcClient);
//Create sample string.
CString str(_T("Wheeee...I am rotating!"));
//Draw transparent, red text.
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(255,0,0));
CFont font;
//font object
LOGFONT stFont; //font definition
//Set font attributes that will not change.
memset(&stFont, 0, sizeof(LOGFONT));
stFont.ifheight=MulDiv(14, -pDC->GetDeviceCaps(LOGPIXELSY), 72);
stFont.ifWeight=FW_NORMAL;
stFont.ifClipPrecision=LCIP_LH_ANGLES;
strcpy(stFont.lfFaceName, "Arial");
//Draw text at 15degree intervals.
for(int nAngle=0 nAngle<3600 nAngle+=150);
{
//Specify new angle.
stFont.lfEscapement=nAngle;
//Create and select font into dc.
font.CreateFontIndirect(&stfont);
CFont* pOldFont=pDC->SelectObject(&font);
//Draw the text.
pDC->SelectObject(pOldFont);
font.DelectObjext();
}
}
(58) 如何正确显示包含标签字符的串
调用GDI文本绘画函数时需要展开标签字符,这可以通过调用CDC::TabbedTextOut或者CDC::DrawText并指定DT_EXPANDTABS标志来完成。TabbedTextOut函数允许指定标签位的数组,下例指定每20设备单位展开一个标签:
void CSampleView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc=GetDocument();
ASSERT_VALID(pDoC);
CString str
str.Format(_T("Cathy\tNorman\tOliver"));
int nTabStop=20 //tabs are every 20 pixels
pDC->TabbedtextOut(10, 10, str, 1, &nTabStop, 10);
}
(59) 如何快速地格式化一个CString对象
调用CString::Format,该函数和printf函数具有相同的参数,下例说明了如何使用Format函数:
//Get size of window.
CRect rcWindow;
GetWindowRect(rcWindow);
//Format message string.
CString strMessage;
strMessage.Format(_T("Window Size(%d, %d)"),
rcWindow.Width(), rcWindow.Height());
//Display the message.
MessageBox(strmessage);
(60) 串太长时如何在其末尾显示一个省略号
调用CDC::DrawText并指定DT_END_ELLIPSIS标志,这样就可以用小略号取代串末尾的字符使其适合于指定的边界矩形。如果要显示路径信息,指定DT_END_ELLIPSIS标志并省略号取代串中间的字符。
void CSampleView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc=GetDocument();
ASSERT_VALID(pDoc);
//Add ellpsis to end of string if it does not fit
pDC->Drawtext(CString("This is a long string"), CRect(10, 10, 80, 30), DT_LEFT | DT_END_ELLIPSIS);
//Add ellpsis to middle of string if it does not fit
pDC->DrawText(AfxgetApp() ->m_pszhelpfilePath, CRect(10, 40, 200, 60), DT_LEFT | DT_PATH_ELLIPSIS);
}
(61) 为什么即使调用EnableMenuItem菜单项后,菜单项还处于禁止状态
需要将CFrameWnd::m_bAutomenuEnable设置为FALSE,如果该数据成员为TRUE(缺省值),工作框将自动地禁止没有ON_UPDATE_COMMAND_UI或者ON_COMMAND的菜单项。
//Disable MFC from automatically disabling menu items.
m_bAuoMenuEnable=FALSE;
//Now enable the menu item.
CMenu* pMenu=GetMenu();
ASSERT_VALID(pMenu);
pMenu->EnableMenuItem(ID_MENU_ITEM,MF_BYCOMMAND | MF_ENABLED);
(62) 如何给系统菜单添加一个菜单项
给系统菜单添加一个菜单项需要进行下述三个步骤:
首先,使用Resource Symbols对话(在View菜单中选择Resource Symbols...可以显示该对话)定义菜单项ID,该ID应大于0x0F而小于0xF000;
其次,调用CWnd::GetSystemMenu获取系统菜单的指针并调用CWnd::Appendmenu将菜单项添加到菜单中。下例给系统菜单添加两个新的
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
…
//Make sure system menu item is in the right range.
ASSERT(IDM_MYSYSITEM &0xFFF0)==IDM_MYSYSITEM);
ASSERT(IDM-MYSYSITEM<0xF000);
//Get pointer to system menu.
CMenu* pSysmenu=GetSystemmenu(FALSE);
ASSERT_VALID(pSysMenu);
//Add a separator and our menu item to system menu.
CString StrMenuItem(_T("New menu item"));
pSysMenu->Appendmenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_MYSYSITEM, strMenuitem);
…
}
现在,选择系统菜单项时用户应进行检测。使用ClassWizard处理WM_SYSCOMMAND消息并检测用户菜单的nID参数:
void CMainFrame::OnSysCommand(UINT nID,LPARAM lParam)
{
//Determine if our system menu item was selected.
if((nID & 0xFFF0)==IDM_MYSYSITEM)
{
//TODO-process system menu item
}
else
CMDIFrameWnd::OnSysCommand(nID, lParam);
}
最后,一个设计良好的UI应用程序应当在系统菜单项加亮时在状态条显示一个帮助信息,这可以通过增加一个包含系统菜单基ID的串表的入口来实现。
(63) 如何确定顶层菜单所占据的菜单行数
这可以通过简单的减法和除法来实现。首先,用户需要计算主框窗口的高度和客户区;其次,从主框窗口的高度中减去客户区、框边界以及标题的高度;最后,除以菜单栏的高度。下例成员函数是一个计算主框菜单所占据的行数的代码实现。
int CMainFrame::GetMenuRows()
{
CRect rcFrame,rcClient;
GetWindowRect(rcFrame);
GetClientRect(rcClient);
return (rcFrame.Height() - rcClient.Height() - ::GetSystemMetrics(SM_CYCAPTION) - (::GetSystemMetrics(SM_CYFRAME) *2)) / ::GetSystemMetrics(SM_CYMENU);
}
(64) 在用户环境中如何确定系统显示元素的颜色
调用SDK函数GetSysColor可以获取一个特定显示元素的颜色。下例说明了如何在MFC函数CMainFrameWnd::OnNcPaint中调用该函数设置窗口标题颜色。
void CMiniFrameWnd::OnNcPaint()
{
…
dc.SetTextColor(::GetSysColor(m_bActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
…
}
(65) 如何查询和设置系统参数
在Windows 3.1 SDK中介绍过SDK函数SystemParametersInfo,调用该函数可以查询和设置系统参数,诸如按键的重复速率设置、鼠标双击延迟时间、图标字体以及桌面覆盖位图等等。
//Create a font that is used for icon titles.
LOGFONT stFont;
∶: SystemParametersInfo(SPIF_GETICONTITLELOGFONT, sizeof(LOGFONT), &stFont, SPIF_SENDWININICHANGE);
m_font.CreateFontIndirect(&stFont);
//Change the wallpaper to leaves.bmp.
::SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, _T(" forest.bmp"), SPIF_UPDATEINIFILE);
(66) 如何确定当前屏幕分辨率
调用SDK函数GetSystemMetrics,该函数可以检索有关windows显示信息,诸如标题大小、边界大小以及滚动条大小等等。
//Initialize CSize object with screen size.
CSize sizeScreen(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
(67) 如何使用一个预定义的Windows光标
调用CWinApp::LoadStandardCursor并传送光标标识符。
BOOL CSampleDialog::OnSetCursor(CWnd* pWnd,UINT nHitTest, UINT message)
{
//Display wait cursor if busy.
if(m_bBusy)
{
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_WAIT));
return TRUE;
}
return CDialog::OnSetCursor(pWnd. nHitTest,message);
}
(68) 如何检索原先的Task Manager应用程序使用的任务列表
原先的Task Manager应用程序显示顶层窗口的列表。为了显示该列表,窗口必须可见、包含一个标题以及不能被其他窗口拥有。调用CWnd::GetWindow可以检索顶层窗口的列表,调用IsWindowVisible、GetWindowTextLength以及GetOwner可以确定窗口是否应该在列表中。下例将把TaskManager窗口的标题填充到列表中。
void GetTaskList(CListBox& list)
{
CString strCaption;
//Caption of window.
list.ResetContent();
//Clear list box.
//Get first Window in window list.
ASSERT_VALID(AfxGetMainWnd());
CWnd* pWnd=AfxGetMainWnd()->GetWindow(GW_HWNDFIRST);
//Walk window list.
while(pWnd)
{
// I window visible, has a caption, and does not have an owner?
if(pWnd->IsWindowVisible()
&& pWnd->GetWindowTextLength()
&& !pWnd->GetOwner())
{
//Add caption o window to list box.
pWnd ->GetWindowText(strCaption);
list.AddString(strCaption);
}
//Get next window in window list.
pWnd=pWnd ->GetWindow(GW_HWNDNEXT);
}
}
(69) 如何确定Windows和Windows系统目录
有两个SDK函数可以完成该功能。GetWindowsDirectory和GetSystemDirectory,下例说明了如何使用这两个函数:
TCHAR szDir [MAX_PATH];
//Get the full path of the windows directory.
::GetWindowsDirectory(szDir, MAX_PATH);
TRACE("Windows directory %s\n", szDir);
//Get the full path of the windows system directory.
::GetSystemDirectory(szDir, MAX_PATH);
TRACE("Windows system directory %s\n", szDir);
(70) 在哪儿创建临文件
调用SDK函数GetTemPath可以确定临时文件的目录,该函数首先为临时路径检测TMP环境变量:如果没有指定TMP,检测TMP环境变量,然后返回到当前目录。下例说明了如何创建一个临时文件。
…
//get unique temporary file.
CString strFile;
GetUniqueTempName(strFile);
TRY
{
//Create file and write data.Note that file is closed
//in the destructor of the CFile object.
CFile file(strFile,CFile::modeCreate | Cfile::modeWrite);
//write data
}
CATCH(CFileException, e);
{
//error opening file
}
END_CATCH
…
Void GetuniqueTempName(CString& strTempName)
{
//Get the temporary files directory.
TCHAR szTempPath [MAX_PATH];
DWORD dwResult=::GetTempPath(MAX_PATH, szTempPath);
ASSERT(dwResult);
//Create a unique temporary file.
TCHAR szTempFile [MAX_PATH];
UINT nResult=GetTempFileName(szTempPath, _T("~ex"),0,szTempfile);
ASSERT(nResult);
strTempName=szTempFile;
}
(71) 我怎样才能建立一个等待光标?
调用BeginWaitCursor函数来启动等待光标,调用EndWaitCursor函数来结束等待光标。要注意,二者都要调用app的成员函数,如下所示:
AfxGetApp()->BeginWaitCursor();
// 要做的事
AfxGetApp()->EndWaitCursor();
(72) 我在MDI框架中有个form视窗。它有个取消按钮,我需要当用户按取消按钮时可关闭form视窗。我应该如何关闭该文档?
调用OnCloseDocument函数。
(73) 如何访问桌面窗口
静态函数CWnd::GetDesktopWindow 返回桌面窗口的指针。下例说明了MFC函数CFrameWnd::BeginModalStae是如何使用该函数进入内部窗口列表的。
void CFrameWnd::BeginModalState()
{
…
//first count all windows that need to be disabled
UINT nCount=0;
HWND hWnd=::GetWindow(::GetDesktopWindow(), GW_CHILD);
while(hWnd!=NULL)
{
if(::IsWindowEnabled(hwnd)
&& CWnd::FromHandlePermanent(hWnd)!=NULL
&& AfxIsDescendant(pParent->m_hWnd, hWnd)
&&::SendMessage(hWnd, WM_DISABLEMODAL, 0, 0)==0)
{
++nCount;
}
hWnd=::GetWindow(hWnd, GW_HWNDNEXT);
}
…
(74) 什么是COLORREF? 我该怎样用它?
COLORREF是一个32-bit整型数值,它代表了一种颜色。你可以使用RGB函数来初始化COLORREF。例如:
COLORREF color = RGB(0, 255, 0);
RGB函数接收三个0-255数值,一个代表红色,一个代表绿色,一个代表蓝色。在上面的例子中,红色和蓝色值都为0,所以在该颜色中没有红色和蓝色。绿色为最大值255。所以该颜色为绿色。0,0,0为黑色,255,255,255为白色。
另一种初始化COLORREF的方法如下所示:
CColorDialog colorDialog;
COLORREF color;
if( colorDialog.DoModal() == IDOK )
{
color = colorDialog.GetColor();
}
这段代码使用了MFC中的颜色对话框,它需要文件。
(75) AppWizard所产生的STDAFX文件是干什么用的?
它主要是协助产生预编译头文件的。通常你是不需要修改它的。
(76) 我在我的程序中是了CDWordArray。我向它添加了约10,000个整数,这使得它变得非常非常慢。为什么会这么糟?
CDWordArray是很好用的,只是因为你没有指定数组的最大尺寸。因此,当你添加新元素时,该类会从堆 中重新分配空间。不幸的是,该类会在每次插入新元素时都为数组重新分配空间。如果你向它添加了很 多新元素,所有这些分配和复制数组的操作会就会使它变慢。解决该问题的方法是,你可以使用SetSize 函数的第二个参数来改变这种重新分配的频率。例如,如果你把该参数设置为500,则每次数组空间超出 时它才重新分配并添加500个新空间,而不是1个。这样一来,你就可以不用重新分配而添加了另外499个 元素空间,这也会大大提高程序的运行速度。
(77) 我该如何改变MDI框架窗口的子窗口的大小以使在窗口以一定的大小打开?
在视中的OnInitialUpdate函数中调用GetParentFrame函数。GetParentFrame会返回一指向一保存有该视的框架窗口的指针。然后调用在框架窗口上调用MoveWindow。
(78) 在我的程序的某些部分,我可以调用 MessageBox 函数来建立一个信息对话框,例如在视类中。但是,在其它部分我却不能,如文档类中。为什么?我怎样才能在我的应用程序类中建立一个信息对话框?
MessageBox函数来自CWnd类,所以你只能在从CWnd继承的类(如CView)中调用它。但是,MFC也提供了AfxMessageBox函数,你可以在任何地方调用它。
(79) 我需要在我的程序中设置全局变量,以使文档中的所有类都能访问。我应该吧它放到哪儿?
把该变量放到该应用程序类的头文件中的attribute处。然后,在程序的任何地方,你都可以用下面的方法来访问该变量:
CMyApp *app =(CMyApp *)AfxGetApp();
app->MyGlobalVariable = ...
(80) 我听说MFC可以发现内存漏洞,我怎样使用该特性?
如果你在Debug菜单中的Go选项(不是Project菜单中的Execute选项)来运行你的应用程序,MFC应该在程序终止时报告内存漏洞。如果没有,那么试试运行MFC Tracer工具程序(在VC++程序组中),并启动跟踪。然后返回应用程序。
调用CWnd::DlgDirList或者CWnd::DlgDirListComboBox,Windows 将自动地向列表框或组合框填充可用的驱动器名或者指定目录中的文件,下例将Windows目录中的文件填充在组合框中:
BOOL CSampleDig::OnInitDialog()
{
CDialog::OnInitDialog();
TCHAR szPath [MAX_PATH] = {"c:\\windows"};
int nReslt = DlgDirListComboBox(szPath, IDC_COMBO, IDC_CURIDIR, DDL_READWRITE | DDL_READONLY | DDL_HIDDEN | DDL_SYSTEM | DDL_ARCHIVE);
return TRUE;
}
(42) 为什么旋转按钮控件看起来倒转
需要调用CSpinCtrl::SetRange 设置旋转按钮控件的范围,旋转按钮控件的缺省上限为0,缺省下限为100,这意味着增加时旋转按控件的值由100变为0。下例将旋转按钮控件的范围设置为0到100:
BOOL CAboutDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//set the lower and upper limit of the spin button
m_wndSpin.SetRange( 0 ,100 );
return TRUE;
}
Visual C++ 4.0 Print对话中的Copise旋转按钮控件也有同样的问题:按下Up按钮时拷贝的数目减少,而按下Down 按钮时拷贝的数目增加。
(43) 为什么旋转按钮控件不能自动地更新它下面的编辑控件
如果使用旋转按钮的autu buddy特性, 则必须保证在对话的标记顺序中buddy窗口优先于旋转按钮控件。从Layout菜单中选择Tab Order菜单项(或者按下Crtl+D)可以设置对话的标签顺序。
(44) 如何用位图显示下压按钮
Windows 95按钮有几处新的创建风格,尤其是BS_BITMAP和BS_ICON,要想具有位图按钮,创建按钮和调用CButton::SetBitmap或CButton::SetIcon时要指定BS_BITMAP或BS_ICON风格。
首先,设置按钮的图标属性。然后,当对话初始化时调用CButton::SetIcon。注意:下例用图标代替位图,使用位图时要小心,因为不知道背景所有的颜色——并非每个人都使用浅灰色。
BOOL CSampleDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//set the images for the push buttons .
BOOL CSampleDlg::OnInitDialog()
{
CDialog::OnInitDialog();
//set the images for the push buttons .
m_wndButton1.SetIcon(AfxGetApp()->LoadIcon(IDI_IPTION1));
m_wndButton2.SetIcon(AfxGetApp()->LoadIcon(IDI_IPTION2));
m_wndButton3.SetIcon(AfxGetApp()->LoadIcon(IDI_IPTION3));
return TRUE;
}
(45) 如何一个创建三态下压按钮
可以使用新的BS_PUSHBUTTON 风格位和检测框以及按钮来创建一个三态下压按钮。这很容易,只需将检测框和按钮拖拉到对话中并指定属性Push—like即可。不用任何附加程序就可以成为三态下压按钮。
(46) 如何动态创建控件
分配一个控件对象的实例并调用其Create成员函数。开发者最容易忽略两件事:忘记指定WS_VISBLE标签和在栈中分配控件对象。下例动态地创建一个下压按钮控件:
//In class declaration(.H file ).
private : CButton* m _pButton;
//In class implementation(.cpp file ) .
m_pButton =new CButton;
ASSERT_VALID(m_pButton);
m_pButton->Create(_T("Button Title ") , WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON. Crect( 0, 0, 100 , 24) , this , IDC_MYBUTTON );
(47) 如何限制编辑框中的准许字符
如果用户在编辑控件中只允许接收数字,可以使用一个标准的编辑控件并指定新的创建标志ES_NUMBERS,它是Windows 95新增加的标志,该标志限制 编辑控件只按收数字字符。如果用户需要复杂的编辑控件,可以使用Microsoft 的屏蔽编辑控件,它是一个很有用的OLE定制控件。
如果希望不使用OLE 定制控件自己处理字符,可以派生一个CEdit类并处理WM_CHAR消息,然后从编辑控件中过滤出特定的字符。首先,使用ClassWizard建立一个 CEdit的派生类,其次,在对话类中指定一个成员变量将编辑控件分类在OnInitdialog 中调用CWnd::SubclassDlgItem .
//In your dialog class declaration(.H file )
private : CMyEdit m_wndEdit // Instance of your new edit control .
//In you dialog class implementation(.CPP file )
BOOL CSampleDialog::OnInitDialog()
{
…
//Subclass the edit lontrod .
m_wndEdit.SubclassDlgItem(IDC_EDIT,this);
…
}
使用ClassWizard处理WM_CHAR消息,计算nChar参量并决定所执行的操作,用户可以确定是否修改、传送字符。下例说明了如何显示字母字符,如果字符是字母字符,则调用CWnd OnChar,否则不调用OnChar.
//Only display alphabetic dharacters .
void CMyEdit::OnChar(UINT nChar , UINT nRepCnt , UITN nFlags )
{
//Determine if nChar is an alphabetic character.
if(::IsCharAlpha((TCHAR) nChar ) )
CEdit::OnChar(nChar, nRepCnt , nFlags);
}
如果要修改字符,则不能仅仅简单地用修改过的nChar调用CEdit::OnChar,然后CEdit::OnChar调用 CWnd::Default获取原来的wParam 和lParam 的值,这样是不行的。要修改一个字符,需要首先修改nChar,然后用修改过的nChar调用CWnd::DefWindowProc。下例说明了如何将字符转变为大写:
//Make all characters uppercase
void CMyEdit::OnChar(UINT nChar , UINT nRepCnt , UINT nFlags )
{
//Make sure character is uppercase .
if(::IsCharAlpha((TCHAR) nChar)
nChar=::CharUpper(nChar);
//Bypass default OnChar processing and directly call default window proc.
DefWindProc(WM_CHAR, nChar , MAKELPARAM(nRepCnt, nFlags));
}
(48) 如何改变控件的颜色
有两种方法。其一,可以在父类中指定控件的颜色,或者利用MFC4.0新的消息反射在控件类中指定颜色。 当控件需要重新着色时,工作框调用父窗口(通常是对话框)的CWnd::OnCrtlColor,可以在父窗口类中重置该函数并指定控件的新的绘画属性。例如,下述代码将对话中的所有编辑控件文本颜色改为红色:
HBRUSH CAboutDig::OnCtlColor(CDC * pDCM , CWnd * pWnd , UINT nCtlColor)
{
HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd , nCtlColor);
//Draw red text for all edit controls .
if(nCtlColor == CTLCOLOR_EDIT)
pDC->SetTextColor(RGB(255, 0, 0));
return hbr;
}
然而,由于每个父窗口必须处理通知消息并指定每个控件的绘画属性,所以,这种方法不是完全的面向对象的方法。控件处理该消息并指定绘画属性更合情合理。消息反射允许用户这样做。通知消息首先发送给父窗口,如果父窗口没有处理则发送给控件。创建一个定制彩色列表框控件必须遵循下述步骤。
首先,使用ClassWizard 创建一个CListBox 的派生类并为该类添加下述数据成员。
class CMyListBox publilc CListBox
{
…
private
COLORREF m_clrFor; // foreground color
COLORREF m_clrBack; //background color
Cbrush m_brush; //background brush
…
}
其次,在类的构造函数中,初始化数据中。
CMyListBox::CMyListBox()
{
//Initialize data members .
m_clrFore = RGB(255 , 255 , 0); //yellow text
m_clrBack = RGB(0 , 0 , 255); // blue background
m_brush.CreateSolidBrush(m_clrBack);
}
最后,使用ClassWizard处理反射的WM_CTLCOLOR(=WM_CTLCOLOR)消息并指定新的绘画属性。
HBRUSH CMyListBox::CtlColor(CDC* pDC, UINT nCtlColor)
{
pDC->SetTextColor(m_clrFore);
pDC->SetBkColor(m_clrBack);
return(HBRUSH) m_brush.GetSafeHandle();
}
现在,控件可以自己决定如何绘画,与父窗口无关。
(49) 当向列表框中添加多个项时如何防止闪烁
调用CWnd::SetRedraw 清除重画标志可以禁止CListBox(或者窗口)重画。当向列表框添加几个项时,用户可以清除重画标志,然后添加项,最后恢复重画标志。为确保重画列表框的新项,调用SetRedraw(TRUE) 之后调用CWnd::Invalidate。
//Disable redrawing.
pListBox->SetRedraw(FALSE);
//Fill in the list box gere
//Enable drwing and make sure list box is redrawn.
pListBox->SetRedraw(TRUE);
pListBox->Invalidate();
(50) 如何向编辑控件中添加文本
由于没有CEdit::AppendText函数,用户只好自己做此项工作。调用CEdit::SetSel移动到编辑控件末尾,然后调用CEdit::ReplaceSel添加文本。下例是AppendText 的一种实现方法:
void CMyEdit::AppendText(LPCSTR pText)
{
int nLen=GetWindowTextLength();
SetFocus();
SetSel(nLen, nLen);
ReplaceSel(pText);
}
(51) 如何访问预定义的GDI对象
可以通过调用CDC::SlectStockObject使用Windows的几个预定义的对象,诸如刷子、笔以及字体。下例使用了Windows预定义的笔和刷子GDI对象在视窗中画一个椭圆。
//Draw ellipse using stock black pen and gray brush.
void CSampleView::OnDraw(CDC* pDC)
{
//Determine size of view.
CRect rcView;
GetClientRect(rcView);
//Use stock black pen and stock gray brush to draw ellipse.
pDC->SelectStockObject(BLACK_PEN);
pDC->SelectStockObject(GRAY_BRUSH);
//Draw the ellipse.
pDC->Ellipse(reView);
}
也可以调用新的SDK函数GetSysColorBrush获取一个系统颜色刷子,下例用背景色在视窗中画一个椭圆:
void CsampleView::OnDraw(CDC* pDC)
{
//Determine size of view.
CRect rcView;
GetClientRect(rcView);
//Use background color for tooltips brush.
CBrush * pOrgBrush=pDC->SelectObject( CBrush::FromHandle(::GetSysColorBrush(COLOR_INFOBK)));
//Draw the ellipse.
pDC->Ellipse(rcView);
//Restore original brush.
pDC->SelectObject(pOrgBrush);
}
(52) 如何获取GDI对象的属性信息
可以调用GDIObject::GetObject。这个函数将指定图表设备的消息写入到缓冲区。下例创建了几个有用的辅助函数。
//Determine if font is bold.
BOOL IsFontBold(const CFont&font)
{
LOGFONT stFont;
font.GetObject(sizeof(LOGFONT), &stFont);
return(stFont.lfBold)? TRUE: FALSE;
}
//Return the size of a bitmap.
CSize GetBitmapSize(const CBitmap&bitmap)
{
BITMAP stBitmap;
bitmap.GetObject(sizeof(BITMAP), &stBitmap);
return CSize(stBitmap.bmWidth, stBitmap.bmHeight);
}
//Create a pen with the same color as a brush.
BOOL CreatePenFromBrush(Cpen&pen, cost Cbrush&brush)
{
LOGBRUSH stBrush;
brush.Getobject(sizeof(LOGBRUSH), &stBrush);
return pen. Createpen(PS_SOLID, 0, stBrush.ibColor);
}
(53) 如何实现一个橡皮区矩形
CRectTracker是一个很有用的类,可以通过调用CRectTracker::TrackRubberBand 响应WM_LBUTTONDOWN消息来创建一个橡皮区矩形。
下例表明使用CRectTracker移动和重置视窗中的蓝色椭圆的大小是很容易的事情。
首先,在文件档中声明一个CRectTracker数据成员:
class CSampleView : Public CView
{
…
public :
CrectTracker m_tracker;
…
}
其次,在文档类的构造函数中初始化CRectTracker 对象:
CSampleDoc::CSampleDOC()
{
//Initialize tracker position, size and style.
m_tracker.m_rect.SetRect(0, 0, 10, 10);
m_tracker.m_nStyle=CRectTracker::resizeInside | CRectTracker::dottedLine;
}
然后,在OnDraw函数中画椭圆和踪迹矩形:
void CSampleView::OnDraw(CDC* pDC)
{
CSampleDoc* pDoc=GetDocument();
ASSERT_VALID(pDoc);
//Select blue brush into device context.
CBrush brush(RGB(0, 0, 255));
CBrush* pOldBrush=pDC->SelectObject(&brush);
//draw ellipse in tracking rectangle.
Crect rcEllipse;
pDoc->m_tracker.GetTrueRect(rcEllipse);
pDC->Ellipse(rcEllipse);
//Draw tracking rectangle.
pDoc->m_tracker.Draw(pDC);
//Select blue brush out of device context.
pDC->Selectobject(pOldBrush);
}
最后,使用ClassWizard处理WM_LBUTTONDOWN消息,并增加下述代码。该段代码根据鼠标击键情况可以拖放、移动或者重置椭圆的大小。
void CSampleView::OnLButtonDown(UINT nFlags, CPoint point)
{
//Get pointer to document.
CSampleDoc* pDoc=GetDocument();
ASSERT_VALID(pDoc);
//If clicked on ellipse, drag or resize it.Otherwise create a
//rubber-band rectangle nd create a new ellipse.
BOOL bResult=pDoc->m_tracker.HitTest(point)!= CRectTracker::hitNothing
//Tracker rectangle changed so update views.
if(bResult)
{
pDoc->m_tracker.Track(this,point,TRue);
pDoc->SetModifiedFlag();
pDoc->UpdateAllViews(NULL);
}
else
pDoc->m-tracker.TrackRubberBand(this,point,TRUE);
CView::onLButtonDown(nFlags,point);
}
(54) 如何更新翻转背景颜色的文本
调用CDC::SetBkmode并传送OPAQUE用当前的背景颜色填充背景,或者调用CDC::SetBkMode并传送TRANSPAARENT使背景保持不变,这两种方法都可以设置背景模式。下例设置背景模式为TRANSPARENT,可以两次更新串,用花色带黑阴影更新文本。黑色串在红色串之后,但由于设置了背景模式仍然可见。
void CSampleView::OnDraw(CDC* pDC)
{
//Determint size of view.
CRect rcView;
GetClientRect(rcVieew);
//Create sample string to display.
CString str(_T("Awesome Shadow Text..."));
//Set the background mode to transparent.
pDC->SetBKMode(TRANSPARENT);
//Draw black shadow text.
rcView.OffsetRect(1, 1);
pDc->SetTextColor(RGB(0, 0, 0));
pDC->DrawText(str, str.GetLength(), rcView, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
//Draw red text.
rcView.OffsetRect(-1,-1);
pDc->SetTextColor(RGB(255, 0, 0));
pDC->DrawText(str, str.GetLength(), rcView, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
}
(55) 如何创建一个具有特定点大小的字体
可以指定字体逻辑单位的大小,但有时指定字体的点的大小可能会更方便一些。可以如下将字体的点转换为字体的高度:
int nHeigth=mulDiv(nPointSize, dc.GetDeviceCaps(LOGPIXELSY), 72);
下例创建了一个8点的Apial字体:
…
CClientDC dc(AqfxGetMainWnd());
m_font.CreateFont(MulDiv(8, dc.GetDeviceCaps(LOGPIXELSY), 72), 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET, OUT_STROKE_PRECIS, CLIP_STROKE_PRECIS, DRAFT_QUALITY, VARIABLE_PITCH | FF-SWISS,_T("Arial"));
(56) 如何计算一个串的大小
函数CDC::GetTextExtent 根据当前选择的字体计算一个串的高度和宽度。如果使用的不是系统字体而是其他字体,则在调用GetTextExtent之前将字体选进设备上下文中是很重要的,否则计算高度和宽度时将依据系统字体,由此得出的结果当然是不正确的。下述样板程序当改变下压按钮的标题时动态调整按钮的大小,按钮的大小由按钮的字体和标题的大小而定。响应消息WM_SETTEXT时调用OnSetText,该消息使用ON_MESSAE宏指令定义的用户自定义消息。
LRESULT CMyButton::OnSettext(WPARAM wParam, LPARAM lParam)
{
//Pass message to window procedure.
LRESULT bResult=CallWindowProc(*GetSuperWndProcAddr(), m_hWnd, GetCurrentMessage()->message,wParam,lParam);
//Get title of push button.
CString strTitle;
GetWindowText(strTitle);
//Select current font into device context.
CDC* pDC=GetDc();
CFont* pFont = GetFont();
CFont* pOldFont = pDC->SelectObject(pFont);
//Calculate size of title.
CSize size=pDC->GetTextExent(strTitle,strTitle.GetLength());
//Adjust the button's size based on its title.
//Add a 5-pixel border around the button.
SetWindowPos(NULL, 0, 0, size.cx+10, size.cy+10, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
//Clean up.
pDC->SelectFont(pOldFont);
ReleaseDC(pDC);
return bResult;
}
(57) 如何显示旋转文本
只要用户使用TrueType或者GDI笔或字体就可以显示旋转文本(有些硬件设备也支持旋转光栅字体)。LOGFONT结构中的 ifEscapement成员指定了文本行和x轴的角度,角度的单位是十分之一度而不是度,例如,ifEscapement为450表示字体旋转45度。为确保所有的字体沿坐标系统的同一方向旋转,一定要设置ifEscapement成员的CLIP_LH_ANGLES位,否则,有些字体可能反向旋转。下例使用了14点Arial字体每间隔15度画一个串。
void CSampleView::OnDraw(CDC* pDC)
{
//Determine the size of the window.
CRect rcClient;
GetClientRect(rcClient);
//Create sample string.
CString str(_T("Wheeee...I am rotating!"));
//Draw transparent, red text.
pDC->SetBkMode(TRANSPARENT);
pDC->SetTextColor(RGB(255,0,0));
CFont font;
//font object
LOGFONT stFont; //font definition
//Set font attributes that will not change.
memset(&stFont, 0, sizeof(LOGFONT));
stFont.ifheight=MulDiv(14, -pDC->GetDeviceCaps(LOGPIXELSY), 72);
stFont.ifWeight=FW_NORMAL;
stFont.ifClipPrecision=LCIP_LH_ANGLES;
strcpy(stFont.lfFaceName, "Arial");
//Draw text at 15degree intervals.
for(int nAngle=0 nAngle<3600 nAngle+=150);
{
//Specify new angle.
stFont.lfEscapement=nAngle;
//Create and select font into dc.
font.CreateFontIndirect(&stfont);
CFont* pOldFont=pDC->SelectObject(&font);
//Draw the text.
pDC->SelectObject(pOldFont);
font.DelectObjext();
}
}
(58) 如何正确显示包含标签字符的串
调用GDI文本绘画函数时需要展开标签字符,这可以通过调用CDC::TabbedTextOut或者CDC::DrawText并指定DT_EXPANDTABS标志来完成。TabbedTextOut函数允许指定标签位的数组,下例指定每20设备单位展开一个标签:
void CSampleView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc=GetDocument();
ASSERT_VALID(pDoC);
CString str
str.Format(_T("Cathy\tNorman\tOliver"));
int nTabStop=20 //tabs are every 20 pixels
pDC->TabbedtextOut(10, 10, str, 1, &nTabStop, 10);
}
(59) 如何快速地格式化一个CString对象
调用CString::Format,该函数和printf函数具有相同的参数,下例说明了如何使用Format函数:
//Get size of window.
CRect rcWindow;
GetWindowRect(rcWindow);
//Format message string.
CString strMessage;
strMessage.Format(_T("Window Size(%d, %d)"),
rcWindow.Width(), rcWindow.Height());
//Display the message.
MessageBox(strmessage);
(60) 串太长时如何在其末尾显示一个省略号
调用CDC::DrawText并指定DT_END_ELLIPSIS标志,这样就可以用小略号取代串末尾的字符使其适合于指定的边界矩形。如果要显示路径信息,指定DT_END_ELLIPSIS标志并省略号取代串中间的字符。
void CSampleView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc=GetDocument();
ASSERT_VALID(pDoc);
//Add ellpsis to end of string if it does not fit
pDC->Drawtext(CString("This is a long string"), CRect(10, 10, 80, 30), DT_LEFT | DT_END_ELLIPSIS);
//Add ellpsis to middle of string if it does not fit
pDC->DrawText(AfxgetApp() ->m_pszhelpfilePath, CRect(10, 40, 200, 60), DT_LEFT | DT_PATH_ELLIPSIS);
}
(61) 为什么即使调用EnableMenuItem菜单项后,菜单项还处于禁止状态
需要将CFrameWnd::m_bAutomenuEnable设置为FALSE,如果该数据成员为TRUE(缺省值),工作框将自动地禁止没有ON_UPDATE_COMMAND_UI或者ON_COMMAND的菜单项。
//Disable MFC from automatically disabling menu items.
m_bAuoMenuEnable=FALSE;
//Now enable the menu item.
CMenu* pMenu=GetMenu();
ASSERT_VALID(pMenu);
pMenu->EnableMenuItem(ID_MENU_ITEM,MF_BYCOMMAND | MF_ENABLED);
(62) 如何给系统菜单添加一个菜单项
给系统菜单添加一个菜单项需要进行下述三个步骤:
首先,使用Resource Symbols对话(在View菜单中选择Resource Symbols...可以显示该对话)定义菜单项ID,该ID应大于0x0F而小于0xF000;
其次,调用CWnd::GetSystemMenu获取系统菜单的指针并调用CWnd::Appendmenu将菜单项添加到菜单中。下例给系统菜单添加两个新的
int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
…
//Make sure system menu item is in the right range.
ASSERT(IDM_MYSYSITEM &0xFFF0)==IDM_MYSYSITEM);
ASSERT(IDM-MYSYSITEM<0xF000);
//Get pointer to system menu.
CMenu* pSysmenu=GetSystemmenu(FALSE);
ASSERT_VALID(pSysMenu);
//Add a separator and our menu item to system menu.
CString StrMenuItem(_T("New menu item"));
pSysMenu->Appendmenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_MYSYSITEM, strMenuitem);
…
}
现在,选择系统菜单项时用户应进行检测。使用ClassWizard处理WM_SYSCOMMAND消息并检测用户菜单的nID参数:
void CMainFrame::OnSysCommand(UINT nID,LPARAM lParam)
{
//Determine if our system menu item was selected.
if((nID & 0xFFF0)==IDM_MYSYSITEM)
{
//TODO-process system menu item
}
else
CMDIFrameWnd::OnSysCommand(nID, lParam);
}
最后,一个设计良好的UI应用程序应当在系统菜单项加亮时在状态条显示一个帮助信息,这可以通过增加一个包含系统菜单基ID的串表的入口来实现。
(63) 如何确定顶层菜单所占据的菜单行数
这可以通过简单的减法和除法来实现。首先,用户需要计算主框窗口的高度和客户区;其次,从主框窗口的高度中减去客户区、框边界以及标题的高度;最后,除以菜单栏的高度。下例成员函数是一个计算主框菜单所占据的行数的代码实现。
int CMainFrame::GetMenuRows()
{
CRect rcFrame,rcClient;
GetWindowRect(rcFrame);
GetClientRect(rcClient);
return (rcFrame.Height() - rcClient.Height() - ::GetSystemMetrics(SM_CYCAPTION) - (::GetSystemMetrics(SM_CYFRAME) *2)) / ::GetSystemMetrics(SM_CYMENU);
}
(64) 在用户环境中如何确定系统显示元素的颜色
调用SDK函数GetSysColor可以获取一个特定显示元素的颜色。下例说明了如何在MFC函数CMainFrameWnd::OnNcPaint中调用该函数设置窗口标题颜色。
void CMiniFrameWnd::OnNcPaint()
{
…
dc.SetTextColor(::GetSysColor(m_bActive ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
…
}
(65) 如何查询和设置系统参数
在Windows 3.1 SDK中介绍过SDK函数SystemParametersInfo,调用该函数可以查询和设置系统参数,诸如按键的重复速率设置、鼠标双击延迟时间、图标字体以及桌面覆盖位图等等。
//Create a font that is used for icon titles.
LOGFONT stFont;
∶: SystemParametersInfo(SPIF_GETICONTITLELOGFONT, sizeof(LOGFONT), &stFont, SPIF_SENDWININICHANGE);
m_font.CreateFontIndirect(&stFont);
//Change the wallpaper to leaves.bmp.
::SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, _T(" forest.bmp"), SPIF_UPDATEINIFILE);
(66) 如何确定当前屏幕分辨率
调用SDK函数GetSystemMetrics,该函数可以检索有关windows显示信息,诸如标题大小、边界大小以及滚动条大小等等。
//Initialize CSize object with screen size.
CSize sizeScreen(GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
(67) 如何使用一个预定义的Windows光标
调用CWinApp::LoadStandardCursor并传送光标标识符。
BOOL CSampleDialog::OnSetCursor(CWnd* pWnd,UINT nHitTest, UINT message)
{
//Display wait cursor if busy.
if(m_bBusy)
{
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_WAIT));
return TRUE;
}
return CDialog::OnSetCursor(pWnd. nHitTest,message);
}
(68) 如何检索原先的Task Manager应用程序使用的任务列表
原先的Task Manager应用程序显示顶层窗口的列表。为了显示该列表,窗口必须可见、包含一个标题以及不能被其他窗口拥有。调用CWnd::GetWindow可以检索顶层窗口的列表,调用IsWindowVisible、GetWindowTextLength以及GetOwner可以确定窗口是否应该在列表中。下例将把TaskManager窗口的标题填充到列表中。
void GetTaskList(CListBox& list)
{
CString strCaption;
//Caption of window.
list.ResetContent();
//Clear list box.
//Get first Window in window list.
ASSERT_VALID(AfxGetMainWnd());
CWnd* pWnd=AfxGetMainWnd()->GetWindow(GW_HWNDFIRST);
//Walk window list.
while(pWnd)
{
// I window visible, has a caption, and does not have an owner?
if(pWnd->IsWindowVisible()
&& pWnd->GetWindowTextLength()
&& !pWnd->GetOwner())
{
//Add caption o window to list box.
pWnd ->GetWindowText(strCaption);
list.AddString(strCaption);
}
//Get next window in window list.
pWnd=pWnd ->GetWindow(GW_HWNDNEXT);
}
}
(69) 如何确定Windows和Windows系统目录
有两个SDK函数可以完成该功能。GetWindowsDirectory和GetSystemDirectory,下例说明了如何使用这两个函数:
TCHAR szDir [MAX_PATH];
//Get the full path of the windows directory.
::GetWindowsDirectory(szDir, MAX_PATH);
TRACE("Windows directory %s\n", szDir);
//Get the full path of the windows system directory.
::GetSystemDirectory(szDir, MAX_PATH);
TRACE("Windows system directory %s\n", szDir);
(70) 在哪儿创建临文件
调用SDK函数GetTemPath可以确定临时文件的目录,该函数首先为临时路径检测TMP环境变量:如果没有指定TMP,检测TMP环境变量,然后返回到当前目录。下例说明了如何创建一个临时文件。
…
//get unique temporary file.
CString strFile;
GetUniqueTempName(strFile);
TRY
{
//Create file and write data.Note that file is closed
//in the destructor of the CFile object.
CFile file(strFile,CFile::modeCreate | Cfile::modeWrite);
//write data
}
CATCH(CFileException, e);
{
//error opening file
}
END_CATCH
…
Void GetuniqueTempName(CString& strTempName)
{
//Get the temporary files directory.
TCHAR szTempPath [MAX_PATH];
DWORD dwResult=::GetTempPath(MAX_PATH, szTempPath);
ASSERT(dwResult);
//Create a unique temporary file.
TCHAR szTempFile [MAX_PATH];
UINT nResult=GetTempFileName(szTempPath, _T("~ex"),0,szTempfile);
ASSERT(nResult);
strTempName=szTempFile;
}
(71) 我怎样才能建立一个等待光标?
调用BeginWaitCursor函数来启动等待光标,调用EndWaitCursor函数来结束等待光标。要注意,二者都要调用app的成员函数,如下所示:
AfxGetApp()->BeginWaitCursor();
// 要做的事
AfxGetApp()->EndWaitCursor();
(72) 我在MDI框架中有个form视窗。它有个取消按钮,我需要当用户按取消按钮时可关闭form视窗。我应该如何关闭该文档?
调用OnCloseDocument函数。
(73) 如何访问桌面窗口
静态函数CWnd::GetDesktopWindow 返回桌面窗口的指针。下例说明了MFC函数CFrameWnd::BeginModalStae是如何使用该函数进入内部窗口列表的。
void CFrameWnd::BeginModalState()
{
…
//first count all windows that need to be disabled
UINT nCount=0;
HWND hWnd=::GetWindow(::GetDesktopWindow(), GW_CHILD);
while(hWnd!=NULL)
{
if(::IsWindowEnabled(hwnd)
&& CWnd::FromHandlePermanent(hWnd)!=NULL
&& AfxIsDescendant(pParent->m_hWnd, hWnd)
&&::SendMessage(hWnd, WM_DISABLEMODAL, 0, 0)==0)
{
++nCount;
}
hWnd=::GetWindow(hWnd, GW_HWNDNEXT);
}
…
(74) 什么是COLORREF? 我该怎样用它?
COLORREF是一个32-bit整型数值,它代表了一种颜色。你可以使用RGB函数来初始化COLORREF。例如:
COLORREF color = RGB(0, 255, 0);
RGB函数接收三个0-255数值,一个代表红色,一个代表绿色,一个代表蓝色。在上面的例子中,红色和蓝色值都为0,所以在该颜色中没有红色和蓝色。绿色为最大值255。所以该颜色为绿色。0,0,0为黑色,255,255,255为白色。
另一种初始化COLORREF的方法如下所示:
CColorDialog colorDialog;
COLORREF color;
if( colorDialog.DoModal() == IDOK )
{
color = colorDialog.GetColor();
}
这段代码使用了MFC中的颜色对话框,它需要文件。
(75) AppWizard所产生的STDAFX文件是干什么用的?
它主要是协助产生预编译头文件的。通常你是不需要修改它的。
(76) 我在我的程序中是了CDWordArray。我向它添加了约10,000个整数,这使得它变得非常非常慢。为什么会这么糟?
CDWordArray是很好用的,只是因为你没有指定数组的最大尺寸。因此,当你添加新元素时,该类会从堆 中重新分配空间。不幸的是,该类会在每次插入新元素时都为数组重新分配空间。如果你向它添加了很 多新元素,所有这些分配和复制数组的操作会就会使它变慢。解决该问题的方法是,你可以使用SetSize 函数的第二个参数来改变这种重新分配的频率。例如,如果你把该参数设置为500,则每次数组空间超出 时它才重新分配并添加500个新空间,而不是1个。这样一来,你就可以不用重新分配而添加了另外499个 元素空间,这也会大大提高程序的运行速度。
(77) 我该如何改变MDI框架窗口的子窗口的大小以使在窗口以一定的大小打开?
在视中的OnInitialUpdate函数中调用GetParentFrame函数。GetParentFrame会返回一指向一保存有该视的框架窗口的指针。然后调用在框架窗口上调用MoveWindow。
(78) 在我的程序的某些部分,我可以调用 MessageBox 函数来建立一个信息对话框,例如在视类中。但是,在其它部分我却不能,如文档类中。为什么?我怎样才能在我的应用程序类中建立一个信息对话框?
MessageBox函数来自CWnd类,所以你只能在从CWnd继承的类(如CView)中调用它。但是,MFC也提供了AfxMessageBox函数,你可以在任何地方调用它。
(79) 我需要在我的程序中设置全局变量,以使文档中的所有类都能访问。我应该吧它放到哪儿?
把该变量放到该应用程序类的头文件中的attribute处。然后,在程序的任何地方,你都可以用下面的方法来访问该变量:
CMyApp *app =(CMyApp *)AfxGetApp();
app->MyGlobalVariable = ...
(80) 我听说MFC可以发现内存漏洞,我怎样使用该特性?
如果你在Debug菜单中的Go选项(不是Project菜单中的Execute选项)来运行你的应用程序,MFC应该在程序终止时报告内存漏洞。如果没有,那么试试运行MFC Tracer工具程序(在VC++程序组中),并启动跟踪。然后返回应用程序。