在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);
}
(47)如何激活变灰的弹出菜单?
在设计菜单时设定为GRAYED的菜单项,如何在运行时激活它?
请看下面的示例代码:
void CMyView::OnRButtonDown(UINT nFlags, CPoint point)
{
CScrollView::OnRButtonDown(nFlags, point);
CMenu *menu, *popup;
menu = new CMenu();
// load menu from resource file
menu->LoadMenu( IDR_POPUPMENU );
popup = menu->GetSubMenu(0); // item 0 is DUMMY
UINT nEnable;
nEnable = MF_BYCOMMAND|MF_GRAYED;
if( your test )
{
nEnable = MF_BYCOMMAND|MF_ENABLED;
}
popup->EnableMenuItem( ID_YOUR_ID, nEnable );
//display menu
ClientToScreen(&point);
popup->TrackPopupMenu(
TPM_LEFTALIGN | TPM_RIGHTBUTTON,
point.x, point.y, this );
delete menu;
}
(48)线程消息?
如何正确地在线程之间传送消息?
下面的代码将会帮你的忙:
void CThread::OnUserOpen( WPARAM wParm, LPARAM lParm )
{
UNUSED( wParm ) ;
UNUSED( lParm ) ;
AfxMessageBox("User Open", MB_OK|MB_ICONEXCLAMATION);
}
当然,也别忘了以下声明:
class CThread : public CWinThread
{
DECLARE_DYNCREATE(CThread)
protected:
CThread(); // protected constructor used by dynamic creation
afx_msg void OnUserOpen( WPARAM wParm, LPARAM lParm );
(49)TreeCtrl控制的显示速度太慢?
我从CTreeCtrl继承了一个TREE控制类,重载主要是为了改写每个节点的文本.我在 OnPaint函数中写了一些代码,但这严重地影响了TREE控制的滚动速度.
OnPaint函数
1.可见节点,对于GetFirstVisibleItem和GetNextVisibleItem来讲,是:
a.根节点;b.父节点已展开的节点;因此,"可见"意味着"没有被未展开的父节点隐藏".当节点滚动到客户外时,它对上述两个函数来讲仍是可见的.
2.当TREE的内容改变时,它默认只将变为可见的节点重绘.另外其它已经是可见的节点没有必要重绘,TREE只是滚动DC的位图而已.
上面的意思是不要绘制你不需要看的节点,那会导致速度降低.建议,测试节点矩形是否在客户区,使得只有需要绘制的节点才会被绘制.
void CIndentTree::OnPaint()
{
CPaintDC dc(this); // device context for painting
HTREEITEM hItem = NULL;
DRAWITEMSTRUCT dis;
CRect rc;
// redraw only visible items with indentation
for(
hItem = GetFirstVisibleItem();
hItem; hItem = GetNextVisibleItem( hItem ) )
{
if( !GetItemRect( hItem, rc, FALSE ) )
continue;
if( rc.top <= dc.m_ps.rcPaint.bottom &&
rc.bottom > dc.m_ps.rcPaint.top &&=20
rc.left <= dc.m_ps.rcPaint.right &&
rc.right > dc.m_ps.rcPaint.left )
{
dis.hwndItem = (HWND)hItem;
dis.rcItem = rc;
OnDrawItem(0, &dis, &dc);
}
}
}
OnDrawItem函数
1.删掉如下代码:
IMAGEINFO* pinfo = new IMAGEINFO;
...
delete pinfo;
没有必要使用动态的IMAGEINFO变量,你可以将其定义为堆栈变量.
2.GetItemState和GetItemText都是使用的GetItem,因此,你只需调用一次, 就可以从节点获得你要的所有信息.
(50)关于工具条?
我需要在程序中做一个FLAT工具条,于是我加入一个变量m_wndToolBar. 在程序主体窗口的OnCreate()函数中修改工具条状态(0,TBSTYLE_FLAT). 在NT中运行正常,为什么在95中工具条变得透明?
在COMCTL32.DLL中的旧版本中有些小bug,绘画时会带来一些问题, 你可以使用一些定制代码,在www.codeguru.com站点上有下载,如果你使用的是6.0版本,你也可以使用下列代码(摘自我的mainfrm.cpp文件)
m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, WS_CHILD | WS_VISIBLE | CBRS_TOP |
CBRS_GRIPPER | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
用CreateEx替换ModifyStyle在同一尺寸的工具条中有些不同((CreateEx 建立的略小些),试一下,如果你仍然有这个问题,检查一下COMCTL32.DLL的版本使用标准按钮替换FLAT.
(51)关于线程消息?
真奇怪,OnUserOpen()函数和OnUserOpen(WPARAM, LPARAM)函数竟然不是一个函数,编译器在查到OnUserOpen(WPARAM, LPARAM)时,不会调用OnUserOpen 莫非有人在消息映象做了什么手脚?
其实,这是MFC严密的表现,处理时,通过函数指针来调用,而该指针是由发生的事件所决定的.如果句柄不正确定义的话,调用当然是非法的
(52)关于控件的焦点?
有谁能给我一个有效的方法:当一个控件失去焦点时,怎样管理才能使对话框的焦点进入到正确的控件.
我有一个可运行的程序来实现,不一定很全面,但能工作.
const int WM_VALIDATE_PARAMS WM_APP + 101;
void CMyDlg::OnKillfocusName()
{
PostMessage(WM_VALIDATE_PARAMS, ED_NAME, 0L);
}
void CMyDlg::OnKillfocusAddress()
{
PostMessage(WM_VALIDATE_PARAMS, ED_ADDRESS, 0L);
}
bool CMyDlg::OnValidateParams(WPARAM rcId, LPARAM)
{
switch( rcId )
{
case ED_NAME:
if( !validateName() )
m_edName.SetFocus();
break;
case ED_ADDRESS:
if( !validateAddress() )
m_edAddress.SetFocus();
break;
default:
break;
}
return true;
}
上面的代码可以在用户使用TAB键或鼠标操纵时,使用焦点跳至下一个控制.当你想DISABLE一个控件或重设焦点时,会有些问题,特别是在Killfocus事件中。
(53)如何捕获键盘按键?
在CTabCtrl的子对话框怎样才能捕获ALT+0组合键
可以在PreTranslateMessage中截取键盘消息。
(54)怎样实现3D效果?
在对话框中怎样实现Edit和Listboxes控件的3D效果?(环境95/NT VC5.0)
1). 使用带WS_EX_CLIENTEDGE标志的::CreateWindowEx来替换::CreateWindow 或者用CWnd::CreateEx替换CWnd::Create.
2).在建立控件之后,调用ModifyStyleEx(0, WS_EX_CLIENTEDGE).
(55)怎样建立客户CSocket?
我有一个客户socket想在socket中建立一个局域联接.我使用下列顺序:
CSocket* m_pSocket;
m_pSocket = new CSMSSocket(this);
m_pSocket->Create();
m_pSocket->Bind(m_intHostPort, m_strHostIPAddress);
m_pSocket->Connect(lpszAddress, nPort);
但每次Windows Socket都定向到别的端口,怎样才能定向到同一个端口(环境:95/NT VC5.0).
1).如果你想用Client Socket,你就不能在connect()之前调用bind(),因为局域端口地址由TCP/IP设置,我们不可能知道下一次将使用那一个端口,我想我们不必这做.
2).看一下Create()的帮助,里面告诉我们必须给Create()指定一个端口值, 缺省的情况为0,也就是由Window为我们选择一个端口,通过Create()将会自动捆绑. 3).我不认为你应该完成所有的工作,但想总是用一个相同的端口来连接远程机器是一个不正确的想法.
问题出在端口数/地址结合必须唯一,如果你想在Create()中指一个固定的端口数,你只能与远程机器建一个单个连接.在你所写的代码中是允许局域端口数可变化,可以打开多个连接来取得相同的地址.在侦听(listening)Socket中有许多理由使用一个固定端口,但在连接(connecting Socket中我想没有太多的必要.
(56)Disable一个非模态对话框的客户区?
我在OCX(对象连接和嵌入客户控制程序)有一个非模态对话框.它有一个菜单以及工具条.现在我想Disable客户区(只是客户区,例如:设置特殊变量时显示一个等待光标,区域里的所有控制都不可以处理)但在客户区的所有控制要看上去没有变化(也就是不可以Disable)
可以这样试一下,建立一个子窗口,覆盖对话框的全部的用户区域,用WS_EX_TRANSPAPENT 透明类型,然后调用函数EnableWindow(FALSE),使用SetClassLong或者别的方法,在子窗口调用"忙"光标,这时光标就正确了,但对话框中的菜单还能正常使用.(说白了就是建立一个透明的子窗口盖住所有的用户区域,然后Disable该透明窗口,在这个窗口中设置光标为"忙") 这个方法我没有试过,但在一些老的Windows的书介绍过这种方法.
(57)关于使用SetClassLong和SetCapture问题
我用SetClassLong设置对话框光标时遇到了一些问题,当我使用SetCapture捕获鼠标时,
光标形状并没有变化时,以下为原代码:
void CMouseMoveSimDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
myDragging = TRUE;
myhPrevCursor = (HCURSOR)SetClassLong( m_hWnd, GCL_HCURSOR,
(long)LoadCursor(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDC_SELECTCURSOR )));
SetCapture(m_hWnd);
CDialog::OnLButtonDown(nFlags, point);
}
如果移去SetCapture这一行,光标就会正确的设置,但它就不能正确的捕获鼠标消息.那儿出问题了(环境NT4.0 VC6.0)?
1).如果我没有记错的话,SetClassLong只影响调用它以后的建立的窗口.可以使用 SetWindowLong来改变已存在的窗口的属性.(为什么要用SetClassLong来改变光标形状, 为什么不在消息WM_SETCURSOR中替换.)
2).我也不清楚问题出在那儿,但下面的方法可以克服SetCapture带来的问题,它是从我的程序里面提出来的:
void CScribbleView::OnLButtonDown(UINT nFlags, CPoint point)
{
........
SetCapture(); // Capture the mouse until button up
myhPrevCursor = (HCURSOR)SetClassLong( m_hWnd, GCL_HCURSOR,
(long)LoadCursor(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDC_CURSOR1)));
SetCursor(LoadCursor(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDC_CURSOR1)));
........
}
void CScribbleView::OnLButtonUp(UINT nFlags, CPoint point)
{
if( GetCapture() != this )
return;
........
ReleaseCapture();
SetClassLong( m_hWnd, GCL_HCURSOR, (long)myhPrevCursor);
return;
}
(58)动画控件?
我在对话框中使用一个动画控件,通常我都是用CAnimated的open成员函数,并加上avi的文件名来使用动画控件,怎样在资源文件加入一个avi文件,作为资源使用?
1).简单,将avi文件引入资源,按你的喜欢来决定是属于那一种类型的,通过ID来代替文件的名字,这样你就可以使用了.
2).在资源窗口中单击右键,在弹出的菜单中选择"Import".这时会打开文件选择框,选择所要的文件,这时系统将会询问自定义资源类型,输入avi.一个AVIS的资源组将会创立,你所选的avi文件将会出现在该组中并拥有一个ID.
3).手动在资源文件中加入一个AVI资源说明,比如:
//在这手工编辑资源文件
IDR_ANIMATION AVI res\animate.avi
(59)错误声明的消息?
我给一个视发送一条消息
pView->SendMessage (MY_MESSAGE, wparam, lparam);
消息声明被认为是不正确的
afx_msg void OnMyMessage();
高手一看就知道这是一条命令错误的声明对象,正确的声明应该为:
afx_msg LERSULT OnMyMessage (WPAPRAM wparam, LPARAM lparam);
因为我不使用参数,程序工作也很好,所以我不知道为什么会有这种错误,该过程处理完之后也没有任何错误的信息出现.但现在release版本中有一个奇怪的现象(debug版本中没有)程序会非正常终止,通常这现象发生在SendMessage()返回之后。为什么?
1.相信问题是出在错误的堆栈上,"thiscall"调用后就应该清除堆栈,调用者调用时将两个参数压入堆栈,但参数却没有被清除.如果你真的不需要WPARAM,LPARAM,也不需要返回值的话,你可以使用ON_MESSAGE_VOID 消息声明.在afxpriv.h中定义,是非文档的,意思就是它不会有什么提示或可能中断程序, 另外,需要注意一下线程消息,注意线程消息是可变的,它们将返回void,没有LRESULT,同样的声明.
2.如果你不使用WParam和LParam,为什么不在视中定义一个用户函数来处理自己想做的?
(59)怎样模拟鼠标动作?
这是困扰我多时的一个问题,怎样才能实现模拟鼠标的动作,就是说要使一个程序实现鼠标的单击,双击,拖放等功能.我认为必须要实现相应的消息传递,但每次都不成功.
比如说,我想关闭记事本窗口,可以传送WM_lBUTTONDOWN和WM_LBUTTONUP(X,Y值为记事本的右上角关闭按钮的位置)给记事本窗口,但窗口并没有关闭.当然,我也知道关闭一个窗口可以通过传送WM_QUIT或WM_CLOSE来实现,但鼠标的消息为什么会丢失?
请教各位大师,怎样模式模拟实现鼠标的动作,或者给我一些怎样发送消息来关闭窗口的建议(不是WM_CLOSE或WM_QUIT)
1).试一下window hooks,你可以使用SetWindowsHookEx和JournalPlayback来处理鼠标事件.
2).你可以使用文档中的SendInput(),它能实现模拟键盘或鼠标事件.如果你使用NT,那也可以用老的函数像mouse_event(),keyb_event等,在Win98中,SendInput()一样可以使用.
3).抱歉不能给你一个满意的回答,你可以在网站http://www.microsoft.com/enable/dev/tooldev.htm 中找到一篇关于模拟输入的文章.
4).在NT中可以使用mouse_event()传递事件,文档上说这种方法已经过时了,那么你可以用 SendInput()替换,但找不到关于此函数的使用说明,所以我依然使用mouse_event,没有任何问题.
(60)改变对话框标题字体?
怎样改变对话框标题文件的字体,改变资源中对话框属性中的字体,将改变所有的控件的字体, 却没有改变标题,但我只想改变标题字体,不改基余控件的属性.是不是我错过一些明显的选项. 通过查找一些MFC代码,我发现有一个CDialog模块,里面调用了一引起字体方法,但该对话框不是公用的,我相信它不会给我任何帮助.
1).就我所知,对话框的标题字体和其它的窗口标题一样,它可以通过系统--显示器--属性--外观来设置,如果自己想这样做,我想你应该取得WM_NCPAINT句柄自己来画出非用户区域(包括标题在内),我从未做这样做过,可能是个错误的方向.
2).如果你是在CView继承的,那你可以在构造函数中看见如下代码:
if( !my_CFont .CreatePointFont( 180,"Helvetica",NULL ) )
return false;
GetEditCtrl().SetFont( &my_CFont ,true )
接下来如果你想改变在对话框中的一个CEdit控件字体时,可以使用以下代码:
if( !my_CFont .CreatePointFont( 180,"Helvetica",NULL ) )
return false;
( GetDlgItem (ID_ANY_CEDIT) ) ->SetFont( &my_CFont );
(61)怎样知道CWinThread对象的状态?
怎样才能知道一个线程是在运行还是已经终止?
可以利用线程句柄所指的::GetExitCodeThread()函数,如果线程已经结束, 它将返回一个退出代码,如果还在运行,则返回一个STILL_ACTIVE.不过在之此前,先将 CWinThread成员对象m_bAutoDelete设置为FALSE.另外对象在线程结束时会自动检测到.
(62)如何调整控件对话框条的大小?
我想让用户能够在控制条出现时控制它的大小,在所有的例子中,在控件浮动时,改变尺寸还可以,但在工具条停靠在框架上时就无法调整其大小,该怎样实现?
1)也许你错过了一些注意点,我用的是codeguru站点上下载的CCoolDialogBar类, 在工具条停靠时也可以重新改变其大小.
2)我开发了一个应用程序,它的界面跟你所说的差不多,让我试着解释一下我是怎样做的.
1.从CDialogBar类中继承一个类,名为CMyBar;
2.在CMyBar中增加一个成员变量,int m_iWidth;
3.在CMyBar中的OnPaint和OnNcPaint中画出工具条(grab bar);
4.拖动工具条时在鼠标事件时绘出轨迹;
5.释放鼠标时,计算CMyBar新宽度.可以通过取得当前轨迹位置,使m_iWidth等于新的宽度;
6.(重要)GetDockingFrame()->RecalcLayout();
7.在CMybar中增加一个成员方法CalcDynamicLayout;
8.在CalcDynamicLayout中,当工具条停靠时,通过计算m_iWidth返回值.
当然,这只是一个很简单的方法,你可以做得比这更好.
3)可以试一下VC6.0中的CReBar类
(63)如何顶端显示CStatic类文字?
我正写一个小的应用程序,我想显示一串文本(CStatic)并且无论别的应用程序运行时是否覆盖,这些文字总会在最上面显示.
1)用CreateEx来建立一个WS_POPUP窗口,使这个窗口总在最上面(always on top) 然后在该窗口中实现文字显示.
2)建立窗口时用SetWindowPos()函数,用&wndTopMost作为第一个参数,这样就可以完成你想做的了.