VC 编程技巧 (六)

来源:互联网 发布:国玺云计算现在怎样了 编辑:程序博客网 时间:2024/05/17 23:42

(1) 怎样知道CWinThread对象的状态?
怎样才能知道一个线程是在运行还是已经终止?

可以利用线程句柄所指的::GetExitCodeThread()函数,如果线程已经结束, 它将返回一个退出代码,如果还在运行,则返回一个STILL_ACTIVE.不过在之此前,先将 CWinThread成员对象m_bAutoDelete设置为FALSE.另外对象在线程结束时会自动检测到.

 

(2) 如何调整控件对话框条的大小?
我想让用户能够在控制条出现时控制它的大小,在所有的例子中,在控件浮动时,改变尺寸还可以,但在工具条停靠在框架上时就无法调整其大小,该怎样实现?

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类


(3) 如何顶端显示CStatic类文字?
我正写一个小的应用程序,我想显示一串文本(CStatic)并且无论别的应用程序运行时是否覆盖,这些文字总会在最上面显示.

1)用CreateEx来建立一个WS_POPUP窗口,使这个窗口总在最上面(always on top) 然后在该窗口中实现文字显示.
2)建立窗口时用SetWindowPos()函数,用&wndTopMost作为第一个参数,这样就可以完成你想做的了.

 

(4) 消息句柄出了什么事?
我在CParentView中为WM_LBUTTONDOWN定义一个句柄,但我建个新的CChildView, 句柄得不到处理.

1)仔细看一下你ChildView文件中的MESSAGE_MAP,可能在第两个参数匹配 BEGIN_MESSAGE_MAP(Child,Parent)中有着错误的基类.如果你是用向导生成器, 那么你很容易就会发生这种事情.
2)检查一下消息映象宏中的类名和父类名是否正确,比如BEGIN_MESSAGE_MAP (CChildView,CParentView).
如果你用自己的消息句柄手工代替了向导所做的,确信你的改动是正确的, 一个错误的参数或者加了一个"const"将会改变消息映象而不会被正确调用.
3)我猜想你一定是用类向导生成器来建立你的CChildView,而且在基类的选择中一定是选了CView,自己动手在消息映象中把它修改过来.


(5) 树形控件为何闪烁?
我从CTreeCtrl中继承了一个类,以缩进的格式显示节点,现在我碰上些问题,当树被重画两次之后(一次为缺省,另一次为对齐文本时)点选节点树就会闪烁.

1)试一下LockWindowUpdate()API函数。
2)试一下加入TVS——HASBUTTONS标志,

ModifyStyleEx(TVS_HASBUTTONS, 0);
....//drawing
ModifyStyleEx(0, TVS_HASBUTTONS);
如果它不再闪烁,那么在将其定义为自画属性,用PreCreateWindow()中加入CS——OWNDC。


(6) 怎样才能关闭树形控件中的滚动条?
我想关闭树形控件的滚动条,但它依然显示出来,怎样才能隐藏它?

1)在建立时加入TVS_NOSCROLL,注意此时你就不可以用键盘来实现翻页,这种类型需要comct32.dll4.71版本以上才可以,并且要在commctrl.h中定义如#define TVS_NOSCROLL 0x2000.
2)值得这样试一下 ModifyStyle(WS_VSCROLL,0),将这段代码放在建立之后,显示之前。

 

(7) 如何建立一个带滚动条的窗口?
我想建立一个带滚动的子窗口,但我没有用向导生成器。

如果你让你的窗口有一个滚动条,你必须首先初始化。如下

   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,它已内建滚动条。

 

(8) 如何实现对话框的拖放?
我有一个对话框程序,想让它实现拖放。但无论用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();

}

 

(9)TrackMouseEvent()怎么了
我使用TrackMouseEvent()函数来跟踪鼠标是否已经离开我的窗口,但在MFC中,如果我使用 ::TrackMouseEvent()系统告诉我没有定义,为什么?

1).请使用_TrackMouseEvent
2).在commctrl.h显示为_TrackMouseEvent(),请注意下划线.
3).可能TrackMouseEvent()不支持Win98(在NT中工作得非常好),建议你结合WM_MOUSEMOVE消息和 SetCapture()函数,当鼠标移出窗口时你依然可以控制.


(10)奇怪的组合框控件

我有一个对话框程序,里面只有几个下拉式给合框.但当鼠标箭头移动到组合框的上下按钮时,会变成"6"或"9",一会儿又恢复到原状,这是为什么?

1)也许是你的操作系统有问题,不防重新起动一次也许就行了(概率非常小8%-())你也可以试一下系统清除工具,如果这事情经常发生,可能你真的需要重装一下95或NT,这也是个好的建议,每隔半年左右可以重装一下系统.
2).我猜想可能是comctl32.dll文件被破坏了.
3).这个问题的原因很有可能是系统的资源不够,你可以试着关闭一些程序、减少屏幕的分辨率来增加一些系统资源。

 

(11)关于使用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);
}
}


(12) 为什么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")来取得用户句柄,剩下的就好办了.

 

(13) 关于复选框的文本颜色
有谁知道怎样才能改变复选框中的文本选项的颜色?

1)你有没有试过在控件中使用OnCtlColor,它将在重画任何控件之前被调用,所以你可以有机会来改变文本选项的颜色。
2)为什么你一定要用PreDrawItem()?你是想在里面做一些特定的代码?我认为DrawItem() 也能处理。在调用重画函数之前取得索引号并改变颜色。

 

(14) 系列化与版本的问题
我需要使用系列化来读取我的文件,为了保证文件能在各个版本中都能实现,我作了尽可能的努力,为什么会不成功.

答:下面的代码是我过去使用过的,希望能对你有所帮助

// 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 );
}

 

(15) 在一个控件内检测并使用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;

/* call GetDlgItem() from an instance of your form view */
pCtrl = ( CMyCtrl * )GetDlgItem( IDC_MYCONTROL );

if( pCtrl != NULL && ::IsWindow( pCtrl->GetSafeHwnd( ) )
pCtrl->SendMessage( WM_COMMAND, /*wParam*/, /*lParam*/ );
// see WM_COMMAND description on help/MSDN for a detailed explanation of
// {W|L}PARAM

 

(16)为何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) 记住一个公共规则,关闭程序前要关闭所有的视。

 

(17) 滚动视中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位。

 

(18) 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之下.

 

(19) 怪异的字体
我们有一个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();
  直接保存一个对象是不安全的。

 

(20) 自画列表框样例
很久以前,有人散发关于自画列表框控件代码,而自画列表框外观就象一个标准列表框,在那时我就有个想法想把程序员开发的所有自画控件的代码惧收集起来,这样程序员们就可以使用现存的代码了。
我想问一下在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/

 

(21) 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将指向一个(程序)开始时的菜单项中的字符串结构。以上方法比较好,因为现在好多程序都使用自定义菜单。

 

(22) 用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,你可以用它来将一个文档分配到相应的窗口上.

 

(23) 怎样取消一个弹出式菜单
我有一个应用程序不显示窗口(建立窗口时使用了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 );

 

原创粉丝点击