VC 常識(六)
(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作為第一個參數,這樣就可以完成你想做的了.
(64)消息句柄出了什麼事?
我在CParentView中為WM_LBUTTONDOWN定義一個句柄,但我建個新的CChildView, 句柄得不到處理.
1)仔細看一下你ChildView檔案中的MESSAGE_MAP,可能在第兩個參數比對 BEGIN_MESSAGE_MAP(Child,Parent)中有着錯誤的基類.如果你是用向導生成器, 那麼你很容易就會發生這種事情.
2)檢查一下消息映象宏中的類名和父類名是否正确,比如BEGIN_MESSAGE_MAP (CChildView,CParentView).
如果你用自己的消息句柄手工代替了向導所做的,确信你的改動是正确的, 一個錯誤的參數或者加了一個"const"将會改變消息映象而不會被正确調用.
3)我猜想你一定是用類向導生成器來建立你的CChildView,而且在基類的選擇中一定是選了CView,自己動手在消息映象中把它修改過來.
(65)樹形控件為何閃爍?
我從CTreeCtrl中繼承了一個類,以縮進的格式顯示節點,現在我碰上些問題,當樹被重畫兩次之後(一次為預設,另一次為對齊文本時)點選節點樹就會閃爍.
1)試一下LockWindowUpdate()API函數。
2)試一下加入TVS——HASBUTTONS标志,
ModifyStyleEx(TVS_HASBUTTONS, 0);
....//drawing
ModifyStyleEx(0, TVS_HASBUTTONS);
如果它不再閃爍,那麼在将其定義為自畫屬性,用PreCreateWindow()中加入CS——OWNDC。
(66)怎樣才能關閉樹形控件中的滾動條?
我想關閉樹形控件的滾動條,但它依然顯示出來,怎樣才能隐藏它?
1)在建立時加入TVS_NOSCROLL,注意此時你就不可以用鍵盤來實作翻頁,這種類型需要comct32.dll4.71版本以上才可以,并且要在commctrl.h中定義如#define TVS_NOSCROLL 0x2000.
2)值得這樣試一下 ModifyStyle(WS_VSCROLL,0),将這段代碼放在建立之後,顯示之前。
(67)如何建立一個帶滾動條的視窗?
我想建立一個帶滾動的子視窗,但我沒有用向導生成器。
如果你讓你的視窗有一個滾動條,你必須首先初始化。如下
SCROLLINFO si;
si.cbSize = sizeof( SCROLLINFO );
si.fMask = SIF_PAGE | SIF_RANGE;
si.nMin = 0;
si.nMax = 100;
si.nPage = 10;
SetScrollInfo( SB_HORZ, &si );
si.nMin = 0;
si.nMax = 50;
si.nPage = 5;
SetScrollInfo( SB_VERT, &si );
如果程式運作時你的視窗内容已經改變或者視窗被改變大小而重畫時,你必須重新設定滾動條。在MFC中包含類CScrollView,它已内建滾動條。
(68)如何實作對話框的拖放?
我有一個對話框程式,想讓它實作拖放。但無論用OnDrag或OnDrop等等,所有的的消息都發送給CView類而不是CDialog類,為什麼?
你應該使用COleDropTarget類,試一下這些:
class CMyOleDropTarget: public COleDropTarget
{
protected:
virtual DROPEFFECT OnDragEnter( CWnd* pWnd, COleDataObject*
pDataObject, DWORD dwKeyState, CPoint point )
{
TRACE( "DRAG Enter/n" );
return DROPEFFECT_MOVE;
};
virtual DROPEFFECT OnDragOver( CWnd* pWnd, COleDataObject*
pDataObject, DWORD dwKeyState, CPoint point )
{
TRACE( "DRAG Over/n" );
return DROPEFFECT_MOVE;
};
};
CMyOleDropTarget DropTarget;
BOOL CDlgDlg::OnInitDialog()
{
CDialog::OnInitDialog();
DropTarget.Register( this );
不要忘記調用AfxOleInit()
BOOL CDlgApp::InitInstance()
{
AfxEnableControlContainer();
AfxOleInit();
}
(69)TrackMouseEvent()怎麼了
我使用TrackMouseEvent()函數來跟蹤滑鼠是否已經離開我的視窗,但在MFC中,如果我使用 ::TrackMouseEvent()系統告訴我沒有定義,為什麼?
1).請使用_TrackMouseEvent
2).在commctrl.h顯示為_TrackMouseEvent(),請注意下劃線.
3).可能TrackMouseEvent()不支援Win98(在NT中工作得非常好),建議你結合WM_MOUSEMOVE消息和 SetCapture()函數,當滑鼠移出視窗時你依然可以控制.
(70)奇怪的組合框控件
我有一個對話框程式,裡面隻有幾個下拉式給合框.但當滑鼠箭頭移動到組合框的上下按鈕時,會變成"6"或"9",一會兒又恢複到原狀,這是為什麼?
1)也許是你的作業系統有問題,不防重新起動一次也許就行了(機率非常小8%-())你也可以試一下系統清除工具,如果這事情經常發生,可能你真的需要重裝一下95或NT,這也是個好的建議,每隔半年左右可以重裝一下系統.
2).我猜想可能是comctl32.dll檔案被破壞了.
3).這個問題的原因很有可能是系統的資源不夠,你可以試着關閉一些程式、減少螢幕的分辨率來增加一些系統資源。
(71)關于使用MS SANS SERIF字型
我看過好多關于建立對話框、組合框等等使用MS SANS SERIF的例子,自己也做過多次。如: m_font.CreatePointFont (80, _T("MS Sans Serif")); 或 m_font.Create (-8, ....., _T("MS Sans Serif")); 那麼想問一下:1)該字型是否在所有的版本中都能實作(包括國際版本) 2)在控制台上有沒有更好的字型代替“SYSTEM”字型?如果有人這樣做了,那又是怎樣設定字型大小等相關設定的?我希望有一個徹底的方法來選擇組合框等的字型。
1)有件事情我做過,在我所有的程式界面中都改變了字型.消息框來顯示使用者選擇的字型. 菜單,工具條以及其他控件的字型都随使用者意願改變.但在對話框中最好還是用對話框編輯器, 其基本字型都是MS SANS SERIF,是以我也以這種字型來作為所有的使用者界面. 以下為我所做的代碼:
// here's the font I use:
SystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
m_fntUI.CreateFontIndirect(&ncm.lfMessageFont);
// here's the code to change the font for a wnd and all it's children, and
resize the controls appropriately
void ChangeDialogFont(CWnd* pWnd, CFont* pFont, int nFlag)
{
CRect windowRect;
// grab old and new text metrics
TEXTMETRIC tmOld, tmNew;
CDC * pDC = pWnd->GetDC();
CFont * pSavedFont = pDC->SelectObject(pWnd->GetFont());
pDC->GetTextMetrics(&tmOld);
pDC->SelectObject(pFont);
pDC->GetTextMetrics(&tmNew);
pDC->SelectObject(pSavedFont);
pWnd->ReleaseDC(pDC);
long oldHeight = tmOld.tmHeight+tmOld.tmExternalLeading;
long newHeight = tmNew.tmHeight+tmNew.tmExternalLeading;
if (nFlag != CDF_NONE)
{
// calculate new dialog window rectangle
CRect clientRect, newClientRect, newWindowRect;
pWnd->GetWindowRect(windowRect);
pWnd->GetClientRect(clientRect);
long xDiff = windowRect.Width() - clientRect.Width();
long yDiff = windowRect.Height() - clientRect.Height();
newClientRect.left = newClientRect.top = 0;
newClientRect.right = clientRect.right * tmNew.tmAveCharWidth /
tmOld.tmAveCharWidth;
newClientRect.bottom = clientRect.bottom * newHeight / oldHeight;
if (nFlag == CDF_TOPLEFT) // resize with origin at top/left of window
{
newWindowRect.left = windowRect.left;
newWindowRect.top = windowRect.top;
newWindowRect.right = windowRect.left + newClientRect.right + xDiff;
newWindowRect.bottom = windowRect.top + newClientRect.bottom + yDiff;
}
else if (nFlag == CDF_CENTER) // resize with origin at center of window
{
newWindowRect.left = windowRect.left -
(newClientRect.right - clientRect.right)/2;
newWindowRect.top = windowRect.top -
(newClientRect.bottom - clientRect.bottom)/2;
newWindowRect.right = newWindowRect.left + newClientRect.right + xDiff;
newWindowRect.bottom = newWindowRect.top + newClientRect.bottom + yDiff;
}
pWnd->MoveWindow(newWindowRect);
}
pWnd->SetFont(pFont);
// iterate through and move all child windows and change their font.
CWnd* pChildWnd = pWnd->GetWindow(GW_CHILD);
while (pChildWnd)
{
pChildWnd->SetFont(pFont);
pChildWnd->GetWindowRect(windowRect);
CString strClass;
::GetClassName(pChildWnd->m_hWnd, strClass.GetBufferSetLength(32), 31);
strClass.MakeUpper();
if(strClass==_T("COMBOBOX"))
{
CRect rect;
pChildWnd->SendMessage(CB_GETDROPPEDCONTROLRECT,0,(LPARAM) &rect);
windowRect.right = rect.right;
windowRect.bottom = rect.bottom;
}
pWnd->ScreenToClient(windowRect);
windowRect.left = windowRect.left * tmNew.tmAveCharWidth /
tmOld.tmAveCharWidth;
windowRect.right = windowRect.right * tmNew.tmAveCharWidth /
tmOld.tmAveCharWidth;
windowRect.top = windowRect.top * newHeight / oldHeight;
windowRect.bottom = windowRect.bottom * newHeight / oldHeight;
pChildWnd->MoveWindow(windowRect);
pChildWnd = pChildWnd->GetWindow(GW_HWNDNEXT);
}
}
(72)為什麼DLL在字元串表中找不到字元串
我用向導生成器中的"Use MFC in a Shared DLL"選項建立一個DLL,在字元串表資源中加一個字元串,當我使用csMyString.LoadString( IDS_MY_STRING ) csMyString 是空的,為什麼會這樣?
1)MFC是由AfxGetResourceHandle調用資源的.是以,如果你想在你的DLL中讀出資源應該使用 AfxSetResourceHandle.你也可以在LoadLibrary的傳回值中得到它,如果不想調用該DLL時也可以使用DLLMain函數的hInstance參數.
2)試一下在你函數打頭處使用AFX_MANAGE_STATE(AfxGetStaticModuleState()) (事實上每個被外部DLL調用的每一個函數都會使用它)
3)我記得先前的清單講過這個問題,試一下以下兩種方法: 如果你是用LoadLibrary()來調用DLL的,它會傳回一個句柄,你可以在 AfxSetResourceHandle()中使用它.如:
hinstnew = Loadbrary(...);
...
hinstOld = AfxGetResourceHandle();
AfxSetResourceHandle(hinstnew);
LoadString(IDS_MY_STRING);
AfxSetResourceHandle(hinstOld); // remember to set this back,
// or your night won't be nice.
如果你不是用LoadLibrary來調用DLL又該怎樣辦呢?你可以使用 GetModule("You DLL Name")來取得使用者句柄,剩下的就好辦了.
(73)關于複選框的文本顔色
有誰知道怎樣才能改變複選框中的文本選項的顔色?
1)你有沒有試過在控件中使用OnCtlColor,它将在重畫任何控件之前被調用,是以你可以有機會來改變文本選項的顔色。
2)為什麼你一定要用PreDrawItem()?你是想在裡面做一些特定的代碼?我認為DrawItem() 也能處理。在調用重畫函數之前取得索引号并改變顔色。
(74)系列化與版本的問題
我需要使用系列化來讀取我的檔案,為了保證檔案能在各個版本中都能實作,我作了盡可能的努力,為什麼會不成功.
答:下面的代碼是我過去使用過的,希望能對你有所幫助
// Use this macro to fix the versioning problem in the MFC
// Place it at the beginning of your CMyObject::Serialize implementation -
// it will guarantee that the correct version of the class is written to
// and read from the archive
//
// Usage: SERIALIZE_VERSION(CMyObject)
#define SERIALIZE_VERSION(this_class) ar.SerializeClass(this_class::GetRuntimeClass());
// For classes which cannot use IMPLEMENT_SERIAL (such as abstract
// base classes). This guarantees the object can have
[Read/Write][Class/Object]
// called on it by placing a schema number in it. It also puts it in the
// list of known class names (AFX_CLASSINIT).
// Note: this is almost the same as IMPLEMENT_SERIAL_ABC
// in "MFC Internals", but this version uses AFX_CLASSINIT,
// with the result that it works!
#define DECLARE_DYNAMIC_SERIAL(class_name) DECLARE_SERIAL(class_name)
#define IMPLEMENT_DYNAMIC_SERIAL(class_name, base_class_name, wSchema) _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, NULL) static const AFX_CLASSINIT
_init_##class_name(RUNTIME_CLASS(class_name)); CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) { pOb = (class_name*)
ar.ReadObject(RUNTIME_CLASS(class_name)); return ar; }
或者也可以這樣實作:
CMySerialRootDerivedClass::Serialize(CArchive& ar)
{
//CMySerialRoot::Serialize(ar); // <- do not call this here
if (ar.IsStoring())
{
... store derived stuff here
}
else
{
int nVersion = ar.GetObjectSchema();
switch(nVersion)
{
case 1:
... load derived version 1 stuff here
break;
case 2:
... load derived version 2 stuff here
break;
default:
// report unknown version of
// this object
break;
}
}
// serialize the base class version information
// -> then serialize the base class
ar.SerializeClass(RUNTIME_CLASS(CMySerialRoot));
CMySerialRoot::Serialize( ar );
}
(75)在一個控件内檢測并使用ON_COMMAND消息
有一個控件(繼承CWnd)在CRormView.可不可以将它的ID在ON_COMMAND消息中發出,如果用pCtrl->OnCommand(ID_VIEW_ZOOMIN,..), 編譯器會報告參數不比對,該怎麼辦?
1)為什麼不用pCtrl->Post/SendMessage (WM_COMMAND, ID_VIEW_ZOOMIN)
2)通過重載CYourFormView::OnCmdMsg就可以.如:
BOOL CYourFormView::OnCmdMsg(UINT nID, int nCode, void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
{
return pCtrl->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)
|| CFormView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
3)使用WM_COMMAND消息,看一下關于WM_COMMAND和CWnd::PostMessage()的幫助.
DWORD wParam;
HIWORD(wParam) = wNotifyCode; // notification code
LOWORD(wParam) = ID_VIEW_ZOOMIN;
pCtrl->PostMessage(WM_COMMAND,(WPARAM)wParam, pCtrl->m_hWnd);
4)能夠這樣做,但不是象你們做法,你們必須得到控件的句柄或CWnd指針然後在句柄中使用::SendMessage() or ::PostMessage();在CWnd中使用 CWnd::SendMessage() or CWnd::PostMessage() 試一下這個.
CMyCtrl *pCtrl;
pCtrl = ( CMyCtrl * )GetDlgItem( IDC_MYCONTROL );
if( pCtrl != NULL && ::IsWindow( pCtrl->GetSafeHwnd( ) )
pCtrl->SendMessage( WM_COMMAND, , );
// see WM_COMMAND description on help/MSDN for a detailed explanation of
// {W|L}PARAM
(76)為何MDI程式中有子視窗打開時主應用程式不能關.
我在MDI程式中增加了一個CRichEditView文檔模闆,在子視窗視中我增加了下面一些代碼.
StartReport (void)
{
CReportFrame *rpt;
CReportDoc *rptDoc;
// First get the right document template
POSITION pPos = theApp.GetFirstDocTemplatePosition();
theApp.GetNextDocTemplate ( pPos );
theApp.GetNextDocTemplate ( pPos );
CDocTemplate *pTemplate = theApp.GetNextDocTemplate ( pPos );
// Verify validity
ASSERT(pTemplate != NULL);
ASSERT_KINDOF(CDocTemplate, pTemplate);
// Create the frame
rptDoc = new CReportDoc;
rpt = (CReportFrame*)pTemplate->CreateNewFrame ( rptDoc, NULL );
pTemplate->InitialUpdateFrame (rpt, rptDoc);
// Get access to the display area
CReportView *rptView = static_cast(rpt->GetActiveView());
CRichEditCtrl &rptCtrl = rptView->GetRichEditCtrl();
}
CReportFrame繼承于CMDIChildWnd
CReportDoc繼承于CRichEditDoc
CReportView繼承于from CRichEditView
如果我關閉程式前不關閉建立的視,調試器将認為程式依然在運作(程式管理器中依然存在) 我需要用調試菜單中的stop debugging來關閉程式;如果我手工關閉該視,程式将會正常關閉.如果有什麼不同的話,在手工關閉新的視之前程式會詢問是否儲存. 那麼怎樣我才能關閉程式呢?
1)我也碰上過對話框,視窗不能自動關閉的情況,這主要是因為繼承的對象不正确所造成的。通常應該在主程式中設定AfxGetMainWnd().
你的程式讓我搞糊塗了,一連使用了多個GetNextDocTemplate(pPos),在這些文檔指針是NULL時通常會引起一些循環.在你的文檔模闆中是否已經精心算好了數目?這樣可能會産生些bugs 我建議找出目前的文檔模闆用CDocTemplate::CreateNewDocument()來代替你的"new CReportDoc"
2) 記住一個公共規則,關閉程式前要關閉所有的視.
(77)滾動視中LPtoDP失敗
在WINDOWS98/95中,當你給光标指針位置大于32767或者小于-21768函數CDC::LPtoDP 将失敗,程式工作在NT上但在95/98中用滾動視工作時卻出現了問題. LPtoDP是在下面函數中被調用的:
SetScrollSizes(MM_HIMETRIC, sizeTotal);
函數是在CScrollView中調用的.我使用的是HIMETRIC映射方式,在我想将A4擴大150%時這個問題就會出現。怎樣才能解決這個問題?
1)在95中确實存在這樣的問題,95中的GDI不是32位的.當我們開發一個程式有編輯矢量圖象時手動而不是由LPtoDP()函數來完成轉換.(在NT中也存在同樣的問題)
2)簡言之,CScrollView或CWnd之是以32位參數會失敗是因為95/98并不是真正的32 位作業系統,裡面仍然包含16位代碼.比如Scrollbars還是隻接受16位的值來調整範圍. NT是一個真正的32位作業系統,就沒有這些困惑.
在95中不得不面對類似的滾動大文檔的問題時,我們隻能另外寫些代碼來實作滾動的實際位置,當它超出-32K或+32K時,你也必須在你的應用中做些映射.
作為一個有關的注意點(可能你已經碰上過這個問題)如果在MFC處理滾動消息時,如: void CSomeWnd::OnVScroll (UINT nSBCode,UINT nPos, CScrollBar *pscrollBar) 中的 nPos參數隻有16位長.克服這個限制可以使用SCROOLINFO結構運作::GetScrollInfo.SCROLLINFO 結構中的nTrackPos是一個真正的32位。
(78)ODBC許可問題
我有個程式想通過ODBC來使用一個MS Access資料庫,但是卻碰上了錯誤,系統顯示 "Records can't be read; no read permission on table SESSION".(記錄不能讀, 表單不允許讀)
)首先我假設access資料庫有一個預設的使用者為"admin",可以這樣完成"ODBC;UID=admin". 然後,當你繼承CRecordset類時你就不必帶參數打開,但下面的方法可能更好些:
Open(CRecordset::dynaset, NULL,CRecordset::useBookmarks | CRecordset::skipDeletedRecords)
當然你必須提供DSN表示連接配接名字的資料庫在ODBC之下.
(79)怪異的字型
我們有一個MFC應用程式,主視窗是在客戶區域内畫些文本和圖形. 我們希望能在客戶區域内顯示文本,在不需要時則擦除.是以我們先得到一個DC(CClientDC), 然後設定字型和文本顔色就開始寫文本,在擦除時,我們用同樣的字型,同樣的地方用背景色重寫文本.
這種方法絕大部分情況下都工作得很好,但偶爾文本并不能完全擦除,有些像素點依然可見. 好象在寫文本時比通常略微胖了些,就象用粗體一樣.字型是在寫文本時使用的,以後也沒有進行過任何的調整. 下面是我們使用的寫與擦除的函數.
void CSign::DrawSignName(CDC* pDC)
{
int OldBkMode;
// select the appropriate font
CFont* pOldFont = (CFont*) pDC->SelectObject(pSignNameFont);
OldBkMode = pDC->SetBkMode(TRANSPARENT);
// determine the colour of the text
if (IsSignNameVisible())
pDC->SetTextColor(aColours[SIGN_NAME_COLOUR]);
else
pDC->SetTextColor(aColours[DEVICE_INVISIBLE_COLOUR]);
// draw the text
pDC->TextOut(m_pointNameCoords.x, m_pointNameCoords.y, m_strName);
// restore the previously used font and background mode
pDC->SelectObject(pOldFont);
pDC->SetBkMode(OldBkMode);
} // DrawSignName
函數是在消息句柄中調用的,而參數中的DC是這樣建立的:
CClientDC dc(AfxGetMainWnd()).
字型是在程式初始化時建立的:
pSignNameFont = new CFont;
pSignNameFont->CreateFont(10,5,0,0,150,
FALSE,FALSE,0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS, "Helvetica");
是不是一次使用兩個指向同一個客戶視窗的DC有問題?程式中的DrawSignName()被多個消息句柄調用。
1)加入以下代碼:
{
m_strName.Empty();
Invalidate();
UpdateWindow();
more stuff;;;
}
上面代碼會産生一個WM_ERASEBKGND消息,将會用背景色填滿視窗,然後再調用OnDraw(),這時隻要将字元串置空即可。
2)我不清楚為什麼程式不能正常工作,但我有個主意(它會更快些)可以在顯示文本的地方用一個背景色的矩形畫一下即可。我也不清楚為什麼你們為什麼要用透明文本,它将會給圖形系統帶來大量的工作。字型之是以有這種情況,是否你們安裝了文本輸出的圖形保真軟體?它會給你們帶來困惑的。
3)你隻想簡單的用一個指針來儲存一個指向DC的GDI對象,并試圖再次調用它時期望它能指向正确的對象。恕我直言,這不是正确的方法(我不知道是否這是顯示不正常的唯一原因)将它轉化為一個Windows句柄才是正确的:
//
// Creating:
//
pSignNameFont = new CFont;
pSignNameFont->CreateFont(10,5,0,0,150,
FALSE,FALSE,0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_SWISS, "Helvetica");
// Now converting into a windows handle
m_hSNFont = (HFONT) pSignNameFont->GetSafeHandle();
直接儲存一個對象是不安全的。
(80)自畫清單框樣例
很久以前,有人散發關于自畫清單框控件代碼,而自畫清單框外觀就象一個标準清單框,在那時我就有個想法想把程式員開發的所有自畫控件的代碼懼收集起來,這樣程式員們就可以使用現存的代碼了。
我想問一下在1996年關于MFC站點那兒有才能關于清單框或其它控件的代碼?
1)自畫清單框代碼如下,看看是不是你所想要的。
Header file
class CCustomListBox : public CListBox
{
public:
// Operations
DECLARE_DYNCREATE(CCustomListBox)
int AddLBItem(LPSTR);
void HandleSelectionState(LPDRAWITEMSTRUCT lpdis);
void HandleFocusState(LPDRAWITEMSTRUCT lpdis);
virtual void DrawItem(LPDRAWITEMSTRUCT lpDIS);
};
cpp file
IMPLEMENT_DYNCREATE(CCustomListBox, CListBox)
int CCustomListBox::AddLBItem(LPSTR itemStr)
{
AddString((LPCSTR)itemStr);
return 0;
}
void CCustomListBox::DrawItem(LPDRAWITEMSTRUCT lpDIS)
{
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
if ((lpDIS->itemState & ODS_SELECTED) &&
(lpDIS->itemAction & (ODA_SELECT | ODA_DRAWENTIRE)))
{
pDC->InvertRect(&lpDIS->rcItem);
pDC->DrawFocusRect(&lpDIS->rcItem);
}
if (!(lpDIS->itemState & ODS_SELECTED) &&
(lpDIS->itemAction & ODA_SELECT))
{
pDC->InvertRect(&lpDIS->rcItem);
pDC->DrawFocusRect(&lpDIS->rcItem);
}
}
void CCustomListBox::HandleSelectionState(LPDRAWITEMSTRUCT lpdis)
{
// Ordinarily could check for "if (lpdis->itemState & ODS_SELECTED)"
// and do drawing for selected state, "else" draw non-selected state.
// But second call to InvertRect restores rectangle to original
// state, so will just call function whether selected or unselected.
::InvertRect (lpdis->hDC, (LPRECT)&lpdis->rcItem);
}
void CCustomListBox::HandleFocusState(LPDRAWITEMSTRUCT lpdis)
{
// Ordinarily would check for "if (lpdis->itemState & ODS_FOCUS)"
// and do drawing for focus state, "else" draw non-focus state.
// But second call to DrawFocusRect restores rectangle to original
// state, so will just call function whether focus or non-focus.
// New to Windows 3.0, this function draws a black dashed-rect
// border on the border of the specified rectangle
::DrawFocusRect( lpdis->hDC, (LPRECT) &lpdis->rcItem );
}
2)http://toronto.planeteer.com/~zalmoxe/
(81)CWnd::GetMenu()的問題
我有個程式用下面代碼:
CWnd *pWnd = CWnd::GeForegroundWindow();
if (pWnd == NULL) return FALSE;
CMenu *pMenu = pWnd->GetMenu();
if (pMenu == NULL) return FALSE;
for (int i = 0; i < pMenu->GetMenuItemCount; i++) {
pMenu->GetMenuItemID(...);
pMenu->GetMenuString(...);
}
上述代碼工作除了在IE視窗外,别的視窗工作都很正常,請問怎樣才能在IE視窗中正常使用,如果不是用這種方法,那又該用什麼方法?
IE有一個定義菜單,是用自定義系列控件中的彈出菜單。是以你就不能再使用枚舉這種方法了,試一下處理WM_INITMENUPOPUP或WM_INITMENU。在VC的CD中有類似的例子(關于剪切與複制)你得到消息句柄時就可以列出所有的菜單項。上面的代碼之所不工作可能是因為微軟的自畫菜單項的儲存菜單項用了不同的格式,想要明白菜單和畫标是否是自畫的,你可以用這種方法測試lpmii->fType & MFT_OWNERDRAW.Ipmii是一個菜單結構,傳回得到的菜單項資訊。lpmii->dwTypeData 傳回(菜單)項目的類型,如果dwTypeData傳回的值沒有什麼用的話還有一個機會,lpmii->dwItemData将指向一個(程式)開始時的菜單項中的字元串結構。以上方法比較好,因為現在好多程式都使用自定義菜單。
(82)用MFC制作彈出視窗
我正在試着用MFC來制作彈出視窗,我看過一些關于建立彈出視窗的文章,它們是使用 CWnd對象的。但在文檔,視窗結構中是怎樣實作的?
你可以建立一個非模态對話框(使用Create函數),你可以在任何建立視窗,子視窗等。如果你一定要在文檔、視窗結構中實作,你也可以用CCreateContest類。下面是建立MDI視窗的例子:
{
LPCTSTR lpszClassName = NULL;
CCreateContext cContext;
cContext.m_pNewViewClass = RUNTIME_CLASS ( CMyView )
DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_OVERLAPPEDWINDOW &
~WS_THICKFRAME & ~WS_MAXIMIZEBOX;
// TOD Add your specialized code here and/or call the base class
if ( CMDIChildWnd::Create(lpszClassName, lpszWindowName, dwStyle,
pParentWnd->rectDefault, pParentWnd, &cContext) )
{
InitialUpdateFrame ( NULL, TRUE );
CScrollView *pView = ( CScrollView* ) GetActiveView();
if ( pView )
pView->ResizeParentToFit ( FALSE );
return TRUE;
}
else
return FALSE;
}
CCreateContext有一個成員為m_pCurrentDoc,你可以用它來将一個文檔配置設定到相應的視窗上.
(83)怎樣取消一個彈出式菜單
我有一個應用程式不顯示視窗(建立視窗時使用了SW_HIDE參數),它隻在任務條顯示一個圖示,我是這樣做的:
NOTIFYICONDATA tnid;
tnid.cbSize = sizeof(NOTIFYICONDATA);
tnid.hWnd = m_hWnd;
tnid.uID = 1;
tnid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
tnid.uCallbackMessage = MYWM_NOTIFYICON;
tnid.hIcon = AfxGetApp()->LoadIcon( IDI_ICON1 );
lstrcpyn(tnid.szTip, "Giroimag Image Mail Exchange", strlen("Giroimag Image Mail Exchange")+1);
Shell_NotifyIcon(NIM_ADD, &tnid);
當我點選任務條時,程式會顯示一個彈出菜單:
CMenu m_Menu;
m_Menu.CreatePopupMenu();
m_Menu.AppendMenu( MF_STRING, IDM_ABOUT, "Op&1" );
m_Menu.AppendMenu( MF_SEPARATOR, 0 );
m_Menu.AppendMenu( MF_STRING, IDM_CONFIG, "Op&2" );
m_Menu.AppendMenu( MF_STRING, IDM_STATUS, ""Op&3" );
m_Menu.AppendMenu( MF_SEPARATOR, 0 );
m_Menu.AppendMenu( MF_STRING, IDM_SEND, "Op&4" );
m_Menu.AppendMenu( MF_STRING, IDM_RECEIVE, "Op&5" );
m_Menu.AppendMenu( MF_SEPARATOR, 0 );
m_Menu.AppendMenu( MF_STRING, IDM_CLOSE, "Op&6" );
POINT p;
GetCursorPos( & p );
m_Menu.TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, this );
到這為止,程式運作很正常,問題在于如果我不選擇任何菜單該怎樣取消它?我以為按ESC或者在菜單外面點選就可以取消,但事實并不是這樣。我也試過用WIN32API中的TrackPopupMenuEx函數但沒有用,到底我該怎麼做?
1)最簡單的方法在消息映象中加"Cancel Menu"指令即可。
2)盡管你的主視窗不可見,但在你可以在調用m_Menu.TrackPopupMenu();時将其置為最前。
3)在你彈出菜單之前,設定你的視窗為最前視窗,調用下面的代碼,問題就會迎刃而解。
POINT p;
GetCursorPos( & p );
// Increase the thread priority by invoking SetForegroundWindow.
SetForegroundWindow();
m_Menu.TrackPopupMenu( TPM_LEFTALIGN | TPM_RIGHTBUTTON, p.x, p.y, this );
4)調用TrackPopupMenu()之前,你必須先調用SetForegroundWindow( m_hWnd ),然後調用PostMessage( m_hWnd, WM_NULL, 0, 0 ):
POINT point;
GetCursorPos( &point );
SetForegroundWindow( m_hWnd );
TrackPopupMenu( hPopup,
TPM_RIGHTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
point.x,
point.y,
0,
m_hWnd, 0 );
PostMessage( m_hWnd, WM_NULL, 0, 0 );